diff --git a/client/ios_handler/in_process_intermediate_dump_handler.cc b/client/ios_handler/in_process_intermediate_dump_handler.cc index b2a1b273..82980583 100644 --- a/client/ios_handler/in_process_intermediate_dump_handler.cc +++ b/client/ios_handler/in_process_intermediate_dump_handler.cc @@ -672,6 +672,8 @@ void InProcessIntermediateDumpHandler::WriteSystemInfo( IntermediateDumpKey::kDaylightName, daylight_name.c_str(), daylight_name.length()); + uint64_t address_mask = system_data.AddressMask(); + WriteProperty(writer, IntermediateDumpKey::kAddressMask, &address_mask); vm_size_t page_size; host_page_size(mach_host_self(), &page_size); diff --git a/minidump/minidump_crashpad_info_writer.cc b/minidump/minidump_crashpad_info_writer.cc index 63e64453..76206a81 100644 --- a/minidump/minidump_crashpad_info_writer.cc +++ b/minidump/minidump_crashpad_info_writer.cc @@ -20,6 +20,7 @@ #include "minidump/minidump_module_crashpad_info_writer.h" #include "minidump/minidump_simple_string_dictionary_writer.h" #include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" #include "util/file/file_writer.h" namespace crashpad { @@ -30,6 +31,7 @@ MinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter() simple_annotations_(nullptr), module_list_(nullptr) { crashpad_info_.version = MinidumpCrashpadInfo::kVersion; + crashpad_info_.reserved = 0; } MinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() { @@ -56,6 +58,10 @@ void MinidumpCrashpadInfoWriter::InitializeFromSnapshot( SetSimpleAnnotations(std::move(simple_annotations)); } + if (process_snapshot->System()) { + SetAddressMask(process_snapshot->System()->AddressMask()); + } + auto modules = std::make_unique(); modules->InitializeFromSnapshot(process_snapshot->Modules()); @@ -90,6 +96,10 @@ void MinidumpCrashpadInfoWriter::SetModuleList( module_list_ = std::move(module_list); } +void MinidumpCrashpadInfoWriter::SetAddressMask(uint64_t mask) { + crashpad_info_.address_mask = mask; +} + bool MinidumpCrashpadInfoWriter::Freeze() { DCHECK_EQ(state(), kStateMutable); diff --git a/minidump/minidump_crashpad_info_writer.h b/minidump/minidump_crashpad_info_writer.h index ac69e454..02907cc8 100644 --- a/minidump/minidump_crashpad_info_writer.h +++ b/minidump/minidump_crashpad_info_writer.h @@ -86,6 +86,9 @@ class MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter { void SetModuleList( std::unique_ptr module_list); + //! \brief Sets MinidumpCrashpadInfo::address_mask. + void SetAddressMask(uint64_t mask); + //! \brief Determines whether the object is useful. //! //! A useful object is one that carries data that makes a meaningful diff --git a/minidump/minidump_crashpad_info_writer_test.cc b/minidump/minidump_crashpad_info_writer_test.cc index d784deaf..39c791c2 100644 --- a/minidump/minidump_crashpad_info_writer_test.cc +++ b/minidump/minidump_crashpad_info_writer_test.cc @@ -122,6 +122,59 @@ TEST(MinidumpCrashpadInfoWriter, ReportAndClientID) { EXPECT_FALSE(module_list); } +TEST(MinidumpCrashpadInfoWriter, AddressMask) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = std::make_unique(); + + constexpr uint64_t mask = 0xFFFFFF8000000000; + crashpad_info_writer->SetAddressMask(mask); + + 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)); + + UUID empty_report_id; + ASSERT_TRUE(empty_report_id.InitializeFromString( + "00000000-0000-0000-0000-000000000000")); + UUID empty_client_id; + ASSERT_TRUE(empty_client_id.InitializeFromString( + "00000000-0000-0000-0000-000000000000")); + + EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion); + EXPECT_EQ(crashpad_info->address_mask, mask); + EXPECT_EQ(crashpad_info->report_id, empty_report_id); + EXPECT_EQ(crashpad_info->client_id, empty_client_id); + EXPECT_FALSE(simple_annotations); + EXPECT_FALSE(module_list); +} + +TEST(MinidumpCrashpadInfoWriter, EmptyAddressMask) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = std::make_unique(); + + 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->address_mask, 0UL); +} + TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) { MinidumpFileWriter minidump_file_writer; auto crashpad_info_writer = std::make_unique(); diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 3cb0ca42..f96cf25b 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -444,8 +444,9 @@ struct ALIGNAS(4) PACKED MinidumpCrashpadInfo { report_id(), client_id(), simple_annotations(), - module_list() { - } + module_list(), + reserved(), + address_mask() {} //! \brief The structure’s currently-defined version number. //! @@ -499,6 +500,28 @@ struct ALIGNAS(4) PACKED MinidumpCrashpadInfo { //! //! This field is present when #version is at least `1`. MINIDUMP_LOCATION_DESCRIPTOR module_list; + + //! \brief This field is always `0`. + uint32_t reserved; + + //! \brief A mask indicating the range of valid addresses for a pointer. + //! + //! ARM64 supports MTE, TBI and PAC masking, generally in the upper bits of + //! a pointer. This mask can be used by LLDB to mimic ptrauth_strip and strip + //! the pointer authentication codes. To recover `pointer` in userland on + //! Darwin, `pointer & (~mask)`. In the case of code running in high memory, + //! where bit 55 is set (indicating that all of the high bits should be set + //! to 1), `pointer | mask`. See ABIMacOSX_arm64::FixAddress for more details + //! here: + //! https://github.com/llvm/llvm-project/blob/001d18664f8bcf63af64f10688809f7681dfbf0b/lldb/source/Plugins/ABI/AArch64/ABIMacOSX_arm64.cpp#L817-L830 + //! + //! If the platform does not support pointer authentication, or the range of + //! valid addressees for a pointer was inaccessible, this field will be 0 and + //! should be ignored. + //! + //! This field is present when #version is at least `1`, if the size of the + //! structure is large enough to accommodate it. + uint64_t address_mask; }; #if defined(COMPILER_MSVC) diff --git a/snapshot/fuchsia/system_snapshot_fuchsia.h b/snapshot/fuchsia/system_snapshot_fuchsia.h index efbe19bd..3d545bb4 100644 --- a/snapshot/fuchsia/system_snapshot_fuchsia.h +++ b/snapshot/fuchsia/system_snapshot_fuchsia.h @@ -73,6 +73,8 @@ class SystemSnapshotFuchsia final : public SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const override; + uint64_t AddressMask() const override { return 0; } + private: std::string os_version_full_; const timeval* snapshot_time_; // weak diff --git a/snapshot/ios/system_snapshot_ios_intermediate_dump.cc b/snapshot/ios/system_snapshot_ios_intermediate_dump.cc index 324ec65e..be676857 100644 --- a/snapshot/ios/system_snapshot_ios_intermediate_dump.cc +++ b/snapshot/ios/system_snapshot_ios_intermediate_dump.cc @@ -57,6 +57,7 @@ SystemSnapshotIOSIntermediateDump::SystemSnapshotIOSIntermediateDump() daylight_offset_seconds_(0), standard_name_(), daylight_name_(), + address_mask_(0), initialized_() {} SystemSnapshotIOSIntermediateDump::~SystemSnapshotIOSIntermediateDump() {} @@ -97,6 +98,8 @@ void SystemSnapshotIOSIntermediateDump::Initialize( dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime; } + GetDataValueFromMap(system_data, Key::kAddressMask, &address_mask_); + vm_size_t page_size; if (GetDataValueFromMap(system_data, Key::kPageSize, &page_size)) { const IOSIntermediateDumpMap* vm_stat = @@ -241,5 +244,10 @@ void SystemSnapshotIOSIntermediateDump::TimeZone( daylight_name->assign(daylight_name_); } +uint64_t SystemSnapshotIOSIntermediateDump::AddressMask() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_mask_; +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/ios/system_snapshot_ios_intermediate_dump.h b/snapshot/ios/system_snapshot_ios_intermediate_dump.h index 4c9ef118..6cc09ac7 100644 --- a/snapshot/ios/system_snapshot_ios_intermediate_dump.h +++ b/snapshot/ios/system_snapshot_ios_intermediate_dump.h @@ -73,6 +73,7 @@ class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const override; + uint64_t AddressMask() const override; private: std::string os_version_build_; @@ -91,6 +92,7 @@ class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot { int daylight_offset_seconds_; std::string standard_name_; std::string daylight_name_; + uint64_t address_mask_; InitializationStateDcheck initialized_; }; diff --git a/snapshot/linux/system_snapshot_linux.h b/snapshot/linux/system_snapshot_linux.h index 4b45a451..75dea39e 100644 --- a/snapshot/linux/system_snapshot_linux.h +++ b/snapshot/linux/system_snapshot_linux.h @@ -89,6 +89,7 @@ class SystemSnapshotLinux final : public SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const override; + uint64_t AddressMask() const override { return 0; } private: void ReadKernelVersion(const std::string& version_string); diff --git a/snapshot/mac/system_snapshot_mac.cc b/snapshot/mac/system_snapshot_mac.cc index 00d777e4..3b17252d 100644 --- a/snapshot/mac/system_snapshot_mac.cc +++ b/snapshot/mac/system_snapshot_mac.cc @@ -390,5 +390,19 @@ void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status, daylight_name); } +uint64_t SystemSnapshotMac::AddressMask() const { + uint64_t mask = 0; +#if defined(ARCH_CPU_ARM64) + // `machdep.virtual_address_size` is the number of addressable bits in + // userspace virtual addresses + uint8_t addressable_bits = + CastIntSysctlByName("machdep.virtual_address_size", 0); + if (addressable_bits) { + mask = ~((1UL << addressable_bits) - 1); + } +#endif + return mask; +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/mac/system_snapshot_mac.h b/snapshot/mac/system_snapshot_mac.h index 047ad2b7..cf8fbbe9 100644 --- a/snapshot/mac/system_snapshot_mac.h +++ b/snapshot/mac/system_snapshot_mac.h @@ -83,6 +83,7 @@ class SystemSnapshotMac final : public SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const override; + uint64_t AddressMask() const override; private: std::string os_version_full_; diff --git a/snapshot/minidump/process_snapshot_minidump.cc b/snapshot/minidump/process_snapshot_minidump.cc index 04fc2711..e88a2646 100644 --- a/snapshot/minidump/process_snapshot_minidump.cc +++ b/snapshot/minidump/process_snapshot_minidump.cc @@ -266,7 +266,10 @@ bool ProcessSnapshotMinidump::InitializeCrashpadInfo() { return true; } - if (stream_it->second->DataSize < sizeof(crashpad_info_)) { + constexpr size_t crashpad_info_min_size = + offsetof(decltype(crashpad_info_), reserved); + size_t remaining_data_size = stream_it->second->DataSize; + if (remaining_data_size < crashpad_info_min_size) { LOG(ERROR) << "crashpad_info size mismatch"; return false; } @@ -275,9 +278,34 @@ bool ProcessSnapshotMinidump::InitializeCrashpadInfo() { return false; } - if (!file_reader_->ReadExactly(&crashpad_info_, sizeof(crashpad_info_))) { + if (!file_reader_->ReadExactly(&crashpad_info_, crashpad_info_min_size)) { return false; } + remaining_data_size -= crashpad_info_min_size; + + // Read `reserved` if available. + size_t crashpad_reserved_size = sizeof(crashpad_info_.reserved); + if (remaining_data_size >= crashpad_reserved_size) { + if (!file_reader_->ReadExactly(&crashpad_info_.reserved, + crashpad_reserved_size)) { + return false; + } + remaining_data_size -= crashpad_reserved_size; + } else { + crashpad_info_.reserved = 0; + } + + // Read `address_mask` if available. + size_t crashpad_address_mask_size = sizeof(crashpad_info_.address_mask); + if (remaining_data_size >= crashpad_address_mask_size) { + if (!file_reader_->ReadExactly(&crashpad_info_.address_mask, + crashpad_address_mask_size)) { + return false; + } + remaining_data_size -= crashpad_address_mask_size; + } else { + crashpad_info_.address_mask = 0; + } if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) { LOG(ERROR) << "crashpad_info version mismatch"; diff --git a/snapshot/minidump/process_snapshot_minidump_test.cc b/snapshot/minidump/process_snapshot_minidump_test.cc index 40693641..00fc3f84 100644 --- a/snapshot/minidump/process_snapshot_minidump_test.cc +++ b/snapshot/minidump/process_snapshot_minidump_test.cc @@ -270,6 +270,47 @@ TEST(ProcessSnapshotMinidump, ClientID) { EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty()); } +TEST(ProcessSnapshotMinidump, ReadOldCrashpadInfo) { + 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) - 8)); + crashpad_info_directory.Location.DataSize = sizeof(crashpad_info) - 8; + + 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; diff --git a/snapshot/minidump/system_snapshot_minidump.cc b/snapshot/minidump/system_snapshot_minidump.cc index e6007b1e..abccda31 100644 --- a/snapshot/minidump/system_snapshot_minidump.cc +++ b/snapshot/minidump/system_snapshot_minidump.cc @@ -195,5 +195,11 @@ void SystemSnapshotMinidump::TimeZone(DaylightSavingTimeStatus* dst_status, NOTREACHED(); // https://crashpad.chromium.org/bug/10 } +uint64_t SystemSnapshotMinidump::AddressMask() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/minidump/system_snapshot_minidump.h b/snapshot/minidump/system_snapshot_minidump.h index 6b12ab99..aff42582 100644 --- a/snapshot/minidump/system_snapshot_minidump.h +++ b/snapshot/minidump/system_snapshot_minidump.h @@ -74,6 +74,7 @@ class SystemSnapshotMinidump : public SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const override; + uint64_t AddressMask() const override; private: MINIDUMP_SYSTEM_INFO minidump_system_info_; diff --git a/snapshot/system_snapshot.h b/snapshot/system_snapshot.h index bec638cd..a1186edb 100644 --- a/snapshot/system_snapshot.h +++ b/snapshot/system_snapshot.h @@ -275,6 +275,20 @@ class SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const = 0; + + //! \brief Returns a mask indicating the range of valid addresses for a + //! pointer. + //! + //! ARM64 supports storing pointer authentication codes in the upper bits of + //! a pointer. This mask is generated based on the number of bits in a pointer + //! reserved for the authentication codes. To recover an address from pointer + //! with an authentication code, `AND` this mask with the pointer. If the pac + //! sign extension bit is set, instead `~` and `OR` this mask with the + //! pointer. + //! + //! If the platform does not support pointer authentication, or the range of + //! valid addressees for a pointer was inaccessible, this field will be 0. + virtual uint64_t AddressMask() const = 0; }; } // namespace crashpad diff --git a/snapshot/test/test_system_snapshot.cc b/snapshot/test/test_system_snapshot.cc index 60437599..a46bce12 100644 --- a/snapshot/test/test_system_snapshot.cc +++ b/snapshot/test/test_system_snapshot.cc @@ -36,14 +36,14 @@ TestSystemSnapshot::TestSystemSnapshot() os_version_bugfix_(0), os_version_build_(), os_version_full_(), + address_mask_(0), 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_() { -} + time_zone_daylight_name_() {} TestSystemSnapshot::~TestSystemSnapshot() { } @@ -130,5 +130,9 @@ void TestSystemSnapshot::TimeZone(DaylightSavingTimeStatus* dst_status, *daylight_name = time_zone_daylight_name_; } +uint64_t TestSystemSnapshot::AddressMask() const { + return address_mask_; +} + } // namespace test } // namespace crashpad diff --git a/snapshot/test/test_system_snapshot.h b/snapshot/test/test_system_snapshot.h index fefe61fa..89cd8413 100644 --- a/snapshot/test/test_system_snapshot.h +++ b/snapshot/test/test_system_snapshot.h @@ -90,6 +90,8 @@ class TestSystemSnapshot final : public SystemSnapshot { time_zone_daylight_name_ = daylight_name; } + void SetAddressMask(uint64_t mask) { address_mask_ = mask; } + // SystemSnapshot: CPUArchitecture GetCPUArchitecture() const override; @@ -114,6 +116,7 @@ class TestSystemSnapshot final : public SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const override; + uint64_t AddressMask() const override; private: CPUArchitecture cpu_architecture_; @@ -134,6 +137,7 @@ class TestSystemSnapshot final : public SystemSnapshot { int os_version_bugfix_; std::string os_version_build_; std::string os_version_full_; + uint64_t address_mask_; bool nx_enabled_; std::string machine_description_; DaylightSavingTimeStatus time_zone_dst_status_; diff --git a/snapshot/win/system_snapshot_win.h b/snapshot/win/system_snapshot_win.h index 6fbcd573..fbe2faec 100644 --- a/snapshot/win/system_snapshot_win.h +++ b/snapshot/win/system_snapshot_win.h @@ -79,6 +79,7 @@ class SystemSnapshotWin final : public SystemSnapshot { int* daylight_offset_seconds, std::string* standard_name, std::string* daylight_name) const override; + uint64_t AddressMask() const override { return 0; } private: std::string os_version_full_; diff --git a/util/ios/ios_intermediate_dump_format.h b/util/ios/ios_intermediate_dump_format.h index f8b7bdf7..9fe7ccf9 100644 --- a/util/ios/ios_intermediate_dump_format.h +++ b/util/ios/ios_intermediate_dump_format.h @@ -85,6 +85,7 @@ namespace internal { TD(kFree, 5017) \ TD(kInactive, 5018) \ TD(kWired, 5019) \ + TD(kAddressMask, 5020) \ TD(kThreads, 6000) \ TD(kDebugState, 6001) \ TD(kFloatState, 6002) \ @@ -103,7 +104,6 @@ namespace internal { TD(kMaxValue, 65535) \ // clang-format on - //! \brief They key for items in the intermediate dump file. //! //! These values are persisted to the intermediate crash dump file. Entries diff --git a/util/ios/ios_system_data_collector.h b/util/ios/ios_system_data_collector.h index 4c252f2d..2dfc373a 100644 --- a/util/ios/ios_system_data_collector.h +++ b/util/ios/ios_system_data_collector.h @@ -43,6 +43,7 @@ class IOSSystemDataCollector { const std::string& StandardName() const { return standard_name_; } const std::string& DaylightName() const { return daylight_name_; } bool IsApplicationActive() const { return active_; } + uint64_t AddressMask() const { return address_mask_; } // Currently unused by minidump. int Orientation() const { return orientation_; } @@ -80,6 +81,7 @@ class IOSSystemDataCollector { std::string standard_name_; std::string daylight_name_; ActiveApplicationCallback active_application_callback_; + uint64_t address_mask_; }; } // namespace internal diff --git a/util/ios/ios_system_data_collector.mm b/util/ios/ios_system_data_collector.mm index 74710028..564c8e79 100644 --- a/util/ios/ios_system_data_collector.mm +++ b/util/ios/ios_system_data_collector.mm @@ -102,6 +102,16 @@ IOSSystemDataCollector::IOSSystemDataCollector() #if defined(ARCH_CPU_X86_64) cpu_vendor_ = ReadStringSysctlByName("machdep.cpu.vendor"); #endif + uint32_t addressable_bits = 0; + size_t len = sizeof(uint32_t); + // `machdep.virtual_address_size` is the number of addressable bits in + // userspace virtual addresses + if (sysctlbyname( + "machdep.virtual_address_size", &addressable_bits, &len, NULL, 0) != + 0) { + addressable_bits = 0; + } + address_mask_ = ~((1UL << addressable_bits) - 1); #if TARGET_OS_SIMULATOR // TODO(justincohen): Consider adding board and model information to