From ce93b696b66372ef98fc339c1c5a75f281d2a421 Mon Sep 17 00:00:00 2001 From: Anton Ovchinnikov Date: Mon, 4 Mar 2019 13:13:06 +0100 Subject: [PATCH 001/146] chore: Add Makefile with helpers --- Makefile | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000..b741664b24 --- /dev/null +++ b/Makefile @@ -0,0 +1,25 @@ +SHELL := /bin/bash +PATH := $(PWD)/../depot_tools:$(PATH) + +all: + echo 'Nothing to do' && exit 1 + +build: + gn gen out/Default + ninja -C out/Default +.PHONY: build + +update: + gclient sync + +example: + g++ -g \ + -o example example.cpp \ + -I. -I./third_party/mini_chromium/mini_chromium \ + -std=c++14 \ + -L./out/Default/obj/client -lclient \ + -L./out/Default/obj/util -lutil \ + -L./out/Default/obj/third_party/mini_chromium/mini_chromium/base -lbase \ + -framework Foundation -framework Security -framework CoreText \ + -framework CoreGraphics -framework IOKit -lbsm +.PHONY: example From d751bd057f9dd39d0349a4d5583fbad0296ef7e5 Mon Sep 17 00:00:00 2001 From: Anton Ovchinnikov Date: Mon, 4 Mar 2019 16:31:35 +0100 Subject: [PATCH 002/146] feat: Add attachments support to crashpad handler --- README.md | 6 +++++ client/crashpad_client.h | 13 +++++++++++ handler/handler_main.cc | 47 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/README.md b/README.md index b20777bbf8..0798f000bb 100644 --- a/README.md +++ b/README.md @@ -40,3 +40,9 @@ https://chromium.googlesource.com/crashpad/crashpad. 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 + +File attachments support for MacOS and Windows. + +Based on changes made in https://github.com/Youw/crashpad/, distributed with Apache 2.0 License. diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 8bf43acdaa..3275d58dc2 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -112,6 +112,19 @@ class CrashpadClient { bool restartable, bool asynchronous_start); +#if defined(OS_MACOSX) + bool StartHandlerWithAttachments( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::map& fileAttachments, + const std::vector& arguments, + bool restartable, + bool asynchronous_start); +#endif + #if defined(OS_ANDROID) || DOXYGEN //! \brief Installs a signal handler to execute `/system/bin/app_process` and //! load a Java class in response to a crash. diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 31686b3eb8..6d7850b945 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -149,6 +149,9 @@ void Usage(const base::FilePath& me) { #endif // OS_LINUX || OS_ANDROID " --url=URL send crash reports to this Breakpad server URL,\n" " only if uploads are enabled for the database\n" +#if defined(OS_WIN) || defined(OS_MACOSX) +" --attachment=NAME=PATH attach a copy of a file, along with a crash dump\n" +#endif // OS_WIN || OS_MACOSX " --help display this help and exit\n" " --version output version information and exit\n", me.value().c_str()); @@ -158,6 +161,7 @@ void Usage(const base::FilePath& me) { struct Options { std::map annotations; std::map monitor_self_annotations; + std::map attachments; std::string url; base::FilePath database; base::FilePath metrics_dir; @@ -204,6 +208,33 @@ bool AddKeyValueToMap(std::map* map, return true; } +#if defined(OS_WIN) || defined(OS_MACOSX) +// Overloaded version, to accept base::FilePath as a VALUE. +bool AddKeyValueToMap(std::map* map, + const std::string& key_value, + const char* argument) { + std::string key; + std::string raw_value; + if (!SplitStringFirst(key_value, '=', &key, &raw_value)) { + LOG(ERROR) << argument << " requires NAME=PATH"; + return false; + } + +#ifdef OS_WIN + base::FilePath value(base::UTF8ToUTF16(raw_value)); +#else + base::FilePath value(raw_value); +#endif + + base::FilePath old_value; + if (!MapInsertOrReplace(map, key, value, &old_value)) { + LOG(WARNING) << argument << " has duplicate name " << key + << ", discarding value " << old_value.value().c_str(); + } + return true; +} +#endif // OS_WIN || OS_MACOSX + // 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 @@ -553,6 +584,9 @@ int HandlerMain(int argc, kOptionSanitizationInformation, #endif kOptionURL, +#if defined(OS_WIN) || defined(OS_MACOSX) + kOptionAttachment, +#endif // OS_WIN || OS_MACOSX // Standard options. kOptionHelp = -2, @@ -612,6 +646,9 @@ int HandlerMain(int argc, kOptionSanitizationInformation}, #endif // OS_LINUX || OS_ANDROID {"url", required_argument, nullptr, kOptionURL}, +#if defined(OS_WIN) || defined(OS_MACOSX) + {"attachment", required_argument, nullptr, kOptionAttachment}, +#endif // OS_WIN || O_MACOSX {"help", no_argument, nullptr, kOptionHelp}, {"version", no_argument, nullptr, kOptionVersion}, {nullptr, 0, nullptr, 0}, @@ -749,6 +786,14 @@ int HandlerMain(int argc, options.url = optarg; break; } +#if defined(OS_WIN) || defined(OS_MACOSX) + case kOptionAttachment: { + if (!AddKeyValueToMap(&options.attachments, optarg, "--attachment")) { + return ExitFailure(); + } + break; + } +#endif // OS_WIN || OS_MACOSX case kOptionHelp: { Usage(me); MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly); @@ -875,6 +920,8 @@ int HandlerMain(int argc, #if defined(OS_FUCHSIA) // TODO(scottmg): Process level file attachments, and for all platforms. nullptr, +#elif defined(OS_WIN) || defined(OS_MACOSX) + &options.attachments, #endif user_stream_sources); From 9a7219a27e9d09facb65a338f96e408a6004769f Mon Sep 17 00:00:00 2001 From: Ihor Dutchak Date: Tue, 27 Nov 2018 01:31:49 +0200 Subject: [PATCH 003/146] Add attachments support to macOS crash report database --- client/crash_report_database.cc | 2 +- client/crash_report_database.h | 4 +- client/crash_report_database_mac.mm | 147 +++++++++++++++++- handler/handler_main.cc | 3 - .../linux/crash_report_exception_handler.cc | 20 +++ .../linux/crash_report_exception_handler.h | 7 + handler/mac/crash_report_exception_handler.cc | 20 +++ handler/mac/crash_report_exception_handler.h | 7 + 8 files changed, 198 insertions(+), 12 deletions(-) diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc index d300a8f9ca..b1d0eaf6fe 100644 --- a/client/crash_report_database.cc +++ b/client/crash_report_database.cc @@ -78,7 +78,7 @@ CrashReportDatabase::UploadReport::~UploadReport() { } } -bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath path, +bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath& path, CrashReportDatabase* db) { database_ = db; InitializeAttachments(); diff --git a/client/crash_report_database.h b/client/crash_report_database.h index 1d6a9ed03a..ebd1dc7304 100644 --- a/client/crash_report_database.h +++ b/client/crash_report_database.h @@ -117,8 +117,6 @@ class CrashReportDatabase { //! \brief Adds an attachment to the report. //! - //! \note This function is not yet implemented on macOS or Windows. - //! //! \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 @@ -171,7 +169,7 @@ class CrashReportDatabase { friend class CrashReportDatabaseMac; friend class CrashReportDatabaseWin; - bool Initialize(const base::FilePath path, CrashReportDatabase* database); + bool Initialize(const base::FilePath& path, CrashReportDatabase* database); void InitializeAttachments(); std::unique_ptr reader_; diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index 414dd5a4a4..75a09264fb 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -34,7 +34,9 @@ #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" @@ -46,6 +48,7 @@ constexpr char kWriteDirectory[] = "new"; constexpr char kUploadPendingDirectory[] = "pending"; constexpr char kCompletedDirectory[] = "completed"; +constexpr char kAttachmentsDirectory[] = "attachments"; constexpr char kSettings[] = "settings.dat"; @@ -148,6 +151,10 @@ 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; + + // Build a filepath for the directory for the report to hold attachments. + base::FilePath AttachmentsPath(const UUID& uuid); private: // CrashReportDatabase: @@ -239,6 +246,16 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, const base::FilePath& report_path, base::FilePath* out_path); + //! \brief Cleans any attachments that have no associated report. + void CleanOrphanedAttachments(); + + //! \brief Attempt 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 report identifier which attachments to remove. + void RemoveAttachmentsByUUID(const UUID& uuid); + base::FilePath base_dir_; Settings settings_; bool xattr_new_names_; @@ -249,12 +266,55 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, FileWriter* CrashReportDatabase::NewReport::AddAttachment( const std::string& name) { - // Attachments aren't implemented in the Mac database yet. - return nullptr; + if (!AttachmentNameIsOK(name)) { + LOG(ERROR) << "invalid name for attachment " << name; + return nullptr; + } + + base::FilePath attachments_dir = + static_cast(database_)->AttachmentsPath( + uuid_); + if (!LoggingCreateDirectory( + attachments_dir, FilePermissions::kOwnerOnly, true)) { + return nullptr; + } + + base::FilePath path = attachments_dir.Append(name); + + auto writer = std::make_unique(); + if (!writer->Open( + path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) { + LOG(ERROR) << "could not open " << path.value(); + return nullptr; + } + attachment_writers_.emplace_back(std::move(writer)); + attachment_removers_.emplace_back(ScopedRemoveFile(path)); + return attachment_writers_.back().get(); } void CrashReportDatabase::UploadReport::InitializeAttachments() { - // Attachments aren't implemented in the Mac database yet. + base::FilePath attachments_dir = + static_cast(database_)->AttachmentsPath( + uuid); + DirectoryReader reader; + if (!reader.Open(attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result dir_result; + while ((dir_result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(attachments_dir.Append(filename)); + std::unique_ptr reader(std::make_unique()); + if (!reader->Open(filepath)) { + LOG(ERROR) << "attachment " << filepath.value() + << " couldn't be opened, skipping"; + continue; + } + attachment_readers_.emplace_back(std::move(reader)); + attachment_map_[filename.value()] = attachment_readers_.back().get(); + } } CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path) @@ -285,6 +345,9 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, return false; } + if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kAttachmentsDirectory))) + return false; + if (!settings_.Initialize(base_dir_.Append(kSettings))) return false; @@ -378,6 +441,14 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, } ignore_result(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_) { + ignore_result(remover.release()); + } + Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); Metrics::CrashReportSize(size); @@ -441,11 +512,10 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, if (!ReadReportMetadataLocked(upload_report->file_path, upload_report.get())) return kDatabaseError; - if (!upload_report->reader_->Open(upload_report->file_path)) { + 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()); @@ -541,6 +611,8 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, return kFileSystemError; } + RemoveAttachmentsByUUID(uuid); + return kNoError; } @@ -625,6 +697,22 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, return kNoError; } +int CrashReportDatabaseMac::CleanDatabase(time_t lockfile_ttl) { + (void)lockfile_ttl; + CleanOrphanedAttachments(); + return 0; +} + +base::FilePath CrashReportDatabaseMac::AttachmentsPath(const UUID& uuid) { +#if defined(OS_WIN) + const std::wstring uuid_string = uuid.ToString16(); +#else + const std::string uuid_string = uuid.ToString(); +#endif + + return base_dir_.Append(kAttachmentsDirectory).Append(uuid_string); +} + // static base::ScopedFD CrashReportDatabaseMac::ObtainReportLock( const base::FilePath& path) { @@ -747,6 +835,55 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, return kNoError; } +void CrashReportDatabaseMac::CleanOrphanedAttachments() { + base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory)); + DirectoryReader reader; + if (!reader.Open(root_attachments_dir)) { + LOG(ERROR) << "no attachments dir"; + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + 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(); + continue; + } + + base::FilePath report_path = LocateCrashReport(uuid, kReportStateAny); + if (!report_path.empty()) { + continue; + } + + // Couldn't find a report, assume these attachments are orphaned. + RemoveAttachmentsByUUID(uuid); + } + } +} + +void CrashReportDatabaseMac::RemoveAttachmentsByUUID(const UUID &uuid) { + base::FilePath attachments_dir = AttachmentsPath(uuid); + DirectoryReader reader; + if (!reader.Open(attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(attachments_dir.Append(filename)); + LoggingRemoveFile(filepath); + } + + LoggingRemoveDirectory(attachments_dir); +} + std::unique_ptr InitializeInternal( const base::FilePath& path, bool may_create) { diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 6d7850b945..b223c9095e 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -207,8 +207,6 @@ bool AddKeyValueToMap(std::map* map, } return true; } - -#if defined(OS_WIN) || defined(OS_MACOSX) // Overloaded version, to accept base::FilePath as a VALUE. bool AddKeyValueToMap(std::map* map, const std::string& key_value, @@ -233,7 +231,6 @@ bool AddKeyValueToMap(std::map* map, } return true; } -#endif // OS_WIN || OS_MACOSX // Calls Metrics::HandlerLifetimeMilestone, but only on the first call. This is // to prevent multiple exit events from inadvertently being recorded, which diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index 71a7009b64..2c8fbe82bd 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -35,10 +35,12 @@ CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources) : database_(database), upload_thread_(upload_thread), process_annotations_(process_annotations), + process_attachments_(process_attachments), user_stream_data_sources_(user_stream_data_sources) {} CrashReportExceptionHandler::~CrashReportExceptionHandler() = default; @@ -173,6 +175,24 @@ bool CrashReportExceptionHandler::HandleExceptionWithConnection( return false; } + if (process_attachments_) { + // Note that attachments are read at this point each time rather than once + // so that if the contents of the file has changed it will be re-read for + // each upload (e.g. in the case of a log file). + for (const auto& it : *process_attachments_) { + FileWriter* writer = new_report->AddAttachment(it.first); + if (writer) { + std::string contents; + if (!LoggingReadEntireFile(it.second, &contents)) { + // Not being able to read the file isn't considered fatal, and + // should not prevent the report from being processed. + continue; + } + writer->Write(contents.data(), contents.size()); + } + } + } + UUID uuid; database_status = database_->FinishedWritingCrashReport(std::move(new_report), &uuid); diff --git a/handler/linux/crash_report_exception_handler.h b/handler/linux/crash_report_exception_handler.h index 9951d840dc..1be7015f9f 100644 --- a/handler/linux/crash_report_exception_handler.h +++ b/handler/linux/crash_report_exception_handler.h @@ -18,6 +18,7 @@ #include #include +#include "base/files/file_path.h" #include "base/macros.h" #include "client/crash_report_database.h" #include "handler/crash_report_upload_thread.h" @@ -50,6 +51,10 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { //! To interoperate with Breakpad servers, the recommended practice is to //! specify values for the `"prod"` and `"ver"` keys as process //! annotations. + //! \param[in] process_attachments A map of file name keys to file paths to be + //! included in the report. Each time a report is written, the file paths + //! will be read in their entirety and included in the report using the + //! file name key as the name in the http upload. //! \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 @@ -58,6 +63,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources); ~CrashReportExceptionHandler(); @@ -81,6 +87,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { CrashReportDatabase* database_; // weak CrashReportUploadThread* upload_thread_; // weak const std::map* process_annotations_; // weak + const std::map* process_attachments_; // weak const UserStreamDataSources* user_stream_data_sources_; // weak DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index 9919e9557b..cdceceb644 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -45,10 +45,12 @@ CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources) : database_(database), upload_thread_(upload_thread), process_annotations_(process_annotations), + process_attachments_(process_attachments), user_stream_data_sources_(user_stream_data_sources) {} CrashReportExceptionHandler::~CrashReportExceptionHandler() { @@ -178,6 +180,24 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( return KERN_FAILURE; } + if (process_attachments_) { + // Note that attachments are read at this point each time rather than once + // so that if the contents of the file has changed it will be re-read for + // each upload (e.g. in the case of a log file). + for (const auto& it : *process_attachments_) { + FileWriter* writer = new_report->AddAttachment(it.first); + if (writer) { + std::string contents; + if (!LoggingReadEntireFile(it.second, &contents)) { + // Not being able to read the file isn't considered fatal, and + // should not prevent the report from being processed. + continue; + } + writer->Write(contents.data(), contents.size()); + } + } + } + UUID uuid; database_status = database_->FinishedWritingCrashReport(std::move(new_report), &uuid); diff --git a/handler/mac/crash_report_exception_handler.h b/handler/mac/crash_report_exception_handler.h index 0b44de67b7..a9a9dc71a9 100644 --- a/handler/mac/crash_report_exception_handler.h +++ b/handler/mac/crash_report_exception_handler.h @@ -20,6 +20,7 @@ #include #include +#include "base/files/file_path.h" #include "base/macros.h" #include "client/crash_report_database.h" #include "handler/crash_report_upload_thread.h" @@ -48,6 +49,10 @@ class CrashReportExceptionHandler : public UniversalMachExcServer::Interface { //! To interoperate with Breakpad servers, the recommended practice is to //! specify values for the `"prod"` and `"ver"` keys as process //! annotations. + //! \param[in] process_attachments A map of file name keys to file paths to be + //! included in the report. Each time a report is written, the file paths + //! will be read in their entirety and included in the report using the + //! file name key as the name in the http upload. //! \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 @@ -56,6 +61,7 @@ class CrashReportExceptionHandler : public UniversalMachExcServer::Interface { CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources); ~CrashReportExceptionHandler(); @@ -84,6 +90,7 @@ class CrashReportExceptionHandler : public UniversalMachExcServer::Interface { CrashReportDatabase* database_; // weak CrashReportUploadThread* upload_thread_; // weak const std::map* process_annotations_; // weak + const std::map* process_attachments_; // weak const UserStreamDataSources* user_stream_data_sources_; // weak DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); From a9419f2fb1c426f49a0b2b45ae1c2652e0aaadd3 Mon Sep 17 00:00:00 2001 From: Ihor Dutchak Date: Sat, 7 Jul 2018 00:37:29 +0300 Subject: [PATCH 004/146] Add attachments support to Windows crash report database --- client/crash_report_database.cc | 8 + client/crash_report_database.h | 2 + client/crash_report_database_generic.cc | 8 - client/crash_report_database_win.cc | 147 +++++++++++++++++- handler/win/crash_report_exception_handler.cc | 20 +++ handler/win/crash_report_exception_handler.h | 7 + 6 files changed, 181 insertions(+), 11 deletions(-) diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc index b1d0eaf6fe..0bbee5f267 100644 --- a/client/crash_report_database.cc +++ b/client/crash_report_database.cc @@ -19,6 +19,14 @@ namespace crashpad { +bool CrashReportDatabase::AttachmentNameIsOK(const std::string& name) { + for (const char c : name) { + if (c != '_' && c != '-' && c != '.' && !isalnum(c)) + return false; + } + return true; +} + CrashReportDatabase::Report::Report() : uuid(), file_path(), diff --git a/client/crash_report_database.h b/client/crash_report_database.h index ebd1dc7304..ef181a7c55 100644 --- a/client/crash_report_database.h +++ b/client/crash_report_database.h @@ -395,6 +395,8 @@ class CrashReportDatabase { protected: CrashReportDatabase() {} + static bool AttachmentNameIsOK(const std::string& name); + 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 diff --git a/client/crash_report_database_generic.cc b/client/crash_report_database_generic.cc index 8e93237421..f4c3fbf646 100644 --- a/client/crash_report_database_generic.cc +++ b/client/crash_report_database_generic.cc @@ -42,14 +42,6 @@ UUID UUIDFromReportPath(const base::FilePath& path) { return uuid; } -bool AttachmentNameIsOK(const std::string& name) { - for (const char c : name) { - if (c != '_' && c != '-' && c != '.' && !isalnum(c)) - return false; - } - return true; -} - using OperationStatus = CrashReportDatabase::OperationStatus; constexpr base::FilePath::CharType kSettings[] = diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index 8967770676..af2c0a8240 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -29,6 +29,8 @@ #include "base/strings/stringprintf.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" @@ -39,6 +41,7 @@ namespace { constexpr wchar_t kReportsDirectory[] = L"reports"; constexpr wchar_t kMetadataFileName[] = L"metadata"; +constexpr wchar_t kAttachmentsDirectory[] = L"attachments"; constexpr wchar_t kSettings[] = L"settings.dat"; @@ -600,6 +603,10 @@ class CrashReportDatabaseWin : public CrashReportDatabase { Metrics::CrashSkippedReason reason) override; OperationStatus DeleteReport(const UUID& uuid) override; OperationStatus RequestUpload(const UUID& uuid) override; + int CleanDatabase(time_t lockfile_ttl) override; + + // Build a filepath for the directory for the report to hold attachments. + base::FilePath AttachmentsPath(const UUID& uuid); private: // CrashReportDatabase: @@ -609,6 +616,16 @@ class CrashReportDatabaseWin : public CrashReportDatabase { std::unique_ptr AcquireMetadata(); + //! \brief Cleans any attachments that have no associated report. + void CleanOrphanedAttachments(); + + //! \brief Attempt 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 report identifier which attachments to remove. + void RemoveAttachmentsByUUID(const UUID& uuid); + base::FilePath base_dir_; Settings settings_; InitializationStateDcheck initialized_; @@ -618,12 +635,56 @@ class CrashReportDatabaseWin : public CrashReportDatabase { FileWriter* CrashReportDatabase::NewReport::AddAttachment( const std::string& name) { - // Attachments aren't implemented in the Windows database yet. - return nullptr; + if (!AttachmentNameIsOK(name)) { + LOG(ERROR) << "invalid name for attachment " << name; + return nullptr; + } + + base::FilePath attachments_dir = + static_cast(database_)->AttachmentsPath( + uuid_); + if (!LoggingCreateDirectory( + attachments_dir, FilePermissions::kOwnerOnly, true)) { + return nullptr; + } + + base::FilePath path = attachments_dir.Append(base::UTF8ToUTF16(name)); + + auto writer = std::make_unique(); + if (!writer->Open( + path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) { + LOG(ERROR) << "could not open " << base::UTF16ToUTF8(path.value()); + return nullptr; + } + attachment_writers_.emplace_back(std::move(writer)); + attachment_removers_.emplace_back(ScopedRemoveFile(path)); + return attachment_writers_.back().get(); } void CrashReportDatabase::UploadReport::InitializeAttachments() { - // Attachments aren't implemented in the Windows database yet. + base::FilePath attachments_dir = + static_cast(database_)->AttachmentsPath( + uuid); + DirectoryReader reader; + if (!reader.Open(attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result dir_result; + while ((dir_result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(attachments_dir.Append(filename)); + std::unique_ptr reader(std::make_unique()); + if (!reader->Open(filepath)) { + LOG(ERROR) << "attachment " << base::UTF16ToUTF8(filepath.value()) + << " couldn't be opened, skipping"; + continue; + } + attachment_readers_.emplace_back(std::move(reader)); + attachment_map_[base::UTF16ToUTF8(filename.value())] = + attachment_readers_.back().get(); + } } CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path) @@ -647,6 +708,9 @@ bool CrashReportDatabaseWin::Initialize(bool may_create) { if (!CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory))) return false; + if (!CreateDirectoryIfNecessary(base_dir_.Append(kAttachmentsDirectory))) + return false; + if (!settings_.Initialize(base_dir_.Append(kSettings))) return false; @@ -689,6 +753,14 @@ OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport( ignore_result(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_) { + ignore_result(remover.release()); + } + *uuid = report->ReportID(); Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); @@ -815,6 +887,9 @@ OperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) { << base::UTF16ToUTF8(report_path.value()); return kFileSystemError; } + + RemoveAttachmentsByUUID(uuid); + return kNoError; } @@ -843,6 +918,62 @@ std::unique_ptr CrashReportDatabaseWin::AcquireMetadata() { return Metadata::Create(metadata_file, base_dir_.Append(kReportsDirectory)); } +void CrashReportDatabaseWin::CleanOrphanedAttachments() { + base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory)); + DirectoryReader reader; + if (!reader.Open(root_attachments_dir)) { + LOG(ERROR) << "no attachments dir"; + return; + } + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return; + + base::FilePath filename; + DirectoryReader::Result result; + 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 " + << base::UTF16ToUTF8(filename.value()); + continue; + } + + // Check to see if the report exist. + const ReportDisk* report_disk; + const OperationStatus os = metadata->FindSingleReport(uuid, &report_disk); + if (os != OperationStatus::kReportNotFound) { + continue; + } + + // Couldn't find a report, assume these attachments are orphaned. + RemoveAttachmentsByUUID(uuid); + } + } +} + +void CrashReportDatabaseWin::RemoveAttachmentsByUUID(const UUID &uuid) { + base::FilePath attachments_dir = AttachmentsPath(uuid); + DirectoryReader reader; + if (!reader.Open(attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(attachments_dir.Append(filename)); + LoggingRemoveFile(filepath); + } + + LoggingRemoveDirectory(attachments_dir); +} + std::unique_ptr InitializeInternal( const base::FilePath& path, bool may_create) { @@ -886,6 +1017,16 @@ OperationStatus CrashReportDatabaseWin::RequestUpload(const UUID& uuid) { return kNoError; } +int CrashReportDatabaseWin::CleanDatabase(time_t lockfile_ttl) { + (void)lockfile_ttl; + CleanOrphanedAttachments(); + return 0; +} + +base::FilePath CrashReportDatabaseWin::AttachmentsPath(const UUID& uuid) { + return base_dir_.Append(kAttachmentsDirectory).Append(uuid.ToString16()); +} + // static std::unique_ptr CrashReportDatabase::Initialize( const base::FilePath& path) { diff --git a/handler/win/crash_report_exception_handler.cc b/handler/win/crash_report_exception_handler.cc index d845c446ec..e735cdc99e 100644 --- a/handler/win/crash_report_exception_handler.cc +++ b/handler/win/crash_report_exception_handler.cc @@ -35,10 +35,12 @@ CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources) : database_(database), upload_thread_(upload_thread), process_annotations_(process_annotations), + process_attachments_(process_attachments), user_stream_data_sources_(user_stream_data_sources) {} CrashReportExceptionHandler::~CrashReportExceptionHandler() { @@ -114,6 +116,24 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( return termination_code; } + if (process_attachments_) { + // Note that attachments are read at this point each time rather than once + // so that if the contents of the file has changed it will be re-read for + // each upload (e.g. in the case of a log file). + for (const auto& it : *process_attachments_) { + FileWriter* writer = new_report->AddAttachment(it.first); + if (writer) { + std::string contents; + if (!LoggingReadEntireFile(it.second, &contents)) { + // Not being able to read the file isn't considered fatal, and + // should not prevent the report from being processed. + continue; + } + writer->Write(contents.data(), contents.size()); + } + } + } + UUID uuid; database_status = database_->FinishedWritingCrashReport(std::move(new_report), &uuid); diff --git a/handler/win/crash_report_exception_handler.h b/handler/win/crash_report_exception_handler.h index c2781de388..2439813b38 100644 --- a/handler/win/crash_report_exception_handler.h +++ b/handler/win/crash_report_exception_handler.h @@ -20,6 +20,7 @@ #include #include +#include "base/files/file_path.h" #include "base/macros.h" #include "handler/user_stream_data_source.h" #include "util/win/exception_handler_server.h" @@ -49,6 +50,10 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { //! To interoperate with Breakpad servers, the recommended practice is to //! specify values for the `"prod"` and `"ver"` keys as process //! annotations. + //! \param[in] process_attachments A map of file name keys to file paths to be + //! included in the report. Each time a report is written, the file paths + //! will be read in their entirety and included in the report using the + //! file name key as the name in the http upload. //! \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 @@ -57,6 +62,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, + const std::map* process_attachments, const UserStreamDataSources* user_stream_data_sources); ~CrashReportExceptionHandler(); @@ -75,6 +81,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { CrashReportDatabase* database_; // weak CrashReportUploadThread* upload_thread_; // weak const std::map* process_annotations_; // weak + const std::map* process_attachments_; // weak const UserStreamDataSources* user_stream_data_sources_; // weak DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); From 0630cedeb22459d8823d6f583b7672bb80753eb7 Mon Sep 17 00:00:00 2001 From: Ihor Dutchak Date: Tue, 27 Nov 2018 04:34:32 +0200 Subject: [PATCH 005/146] update CrashReportDatabaseTest --- client/crash_report_database_test.cc | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index 2dbb4fc12c..aed16ebdc9 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -20,7 +20,6 @@ #include "test/errors.h" #include "test/file.h" #include "test/filesystem.h" -#include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" @@ -671,10 +670,6 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) { } TEST_F(CrashReportDatabaseTest, Attachments) { -#if defined(OS_MACOSX) || defined(OS_WIN) - // Attachments aren't supported on Mac and Windows yet. - DISABLED_TEST(); -#else std::unique_ptr new_report; ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); @@ -713,16 +708,9 @@ TEST_F(CrashReportDatabaseTest, Attachments) { 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); -#endif } TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { -#if defined(OS_MACOSX) || defined(OS_WIN) - // Attachments aren't supported on Mac and Windows yet. - DISABLED_TEST(); -#else - // TODO: This is using paths that are specific to the generic implementation - // and will need to be generalized for Mac and Windows. std::unique_ptr new_report; ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); @@ -744,16 +732,25 @@ TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { ASSERT_TRUE(LoggingRemoveFile(report.file_path)); +// Additional check for Generic database +#if !defined(OS_MACOSX) && !defined(OS_WIN) ASSERT_TRUE(LoggingRemoveFile(base::FilePath( report.file_path.RemoveFinalExtension().value() + ".meta"))); +#endif ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kReportNotFound); +#ifdef OS_WIN + auto uuid_str = uuid.ToString16(); +#else + auto uuid_str = uuid.ToString(); +#endif + base::FilePath report_attachments_dir( - path().Append("attachments").Append(uuid.ToString())); - base::FilePath file_path1(report_attachments_dir.Append("file1")); - base::FilePath file_path2(report_attachments_dir.Append("file2")); + path().Append(FILE_PATH_LITERAL("attachments")).Append(uuid_str)); + 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)); @@ -762,7 +759,6 @@ TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { EXPECT_FALSE(FileExists(file_path1)); EXPECT_FALSE(FileExists(file_path2)); EXPECT_FALSE(FileExists(report_attachments_dir)); -#endif } // This test uses knowledge of the database format to break it, so it only From 1fc0ce3a5970944481fe80f7a05b0983ef1b4499 Mon Sep 17 00:00:00 2001 From: Anton Ovchinnikov Date: Mon, 4 Mar 2019 19:54:37 +0100 Subject: [PATCH 006/146] feat: Add StartHandlerWithAttachments with examples --- Makefile | 4 ++ README.md | 4 ++ client/crash_report_database_win.cc | 6 +-- client/crashpad_client.h | 6 +-- client/crashpad_client_mac.cc | 26 +++++++++ client/crashpad_client_win.cc | 27 ++++++++++ example.cpp | 81 +++++++++++++++++++++++++++++ handler/handler_main.cc | 18 +++---- 8 files changed, 157 insertions(+), 15 deletions(-) create mode 100644 example.cpp diff --git a/Makefile b/Makefile index b741664b24..80a41c2b2c 100644 --- a/Makefile +++ b/Makefile @@ -23,3 +23,7 @@ example: -framework Foundation -framework Security -framework CoreText \ -framework CoreGraphics -framework IOKit -lbsm .PHONY: example + +gen-sentry-patch: + git format-patch --stdout master...HEAD > getsentry.patch +.PHONY: get-sentry-patch diff --git a/README.md b/README.md index 0798f000bb..1da5384fd5 100644 --- a/README.md +++ b/README.md @@ -46,3 +46,7 @@ https://chromium.googlesource.com/crashpad/crashpad. File attachments support for MacOS and Windows. Based on changes made in https://github.com/Youw/crashpad/, distributed with Apache 2.0 License. + +Generating patch: + + git format-patch --stdout master...HEAD > getsentry.patch diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index af2c0a8240..a78b7cee03 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -675,13 +675,13 @@ void CrashReportDatabase::UploadReport::InitializeAttachments() { while ((dir_result = reader.NextFile(&filename)) == DirectoryReader::Result::kSuccess) { const base::FilePath filepath(attachments_dir.Append(filename)); - std::unique_ptr reader(std::make_unique()); - if (!reader->Open(filepath)) { + std::unique_ptr fileReader(std::make_unique()); + if (!fileReader->Open(filepath)) { LOG(ERROR) << "attachment " << base::UTF16ToUTF8(filepath.value()) << " couldn't be opened, skipping"; continue; } - attachment_readers_.emplace_back(std::move(reader)); + attachment_readers_.emplace_back(std::move(fileReader)); attachment_map_[base::UTF16ToUTF8(filename.value())] = attachment_readers_.back().get(); } diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 3275d58dc2..7d91b59577 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -112,18 +112,18 @@ class CrashpadClient { bool restartable, bool asynchronous_start); -#if defined(OS_MACOSX) +#if defined(OS_WIN) || defined(OS_MACOSX) bool StartHandlerWithAttachments( const base::FilePath& handler, const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, const std::map& annotations, - const std::map& fileAttachments, + const std::map& fileAttachments, const std::vector& arguments, bool restartable, bool asynchronous_start); -#endif +#endif // OS_WIN || OS_MACOSX #if defined(OS_ANDROID) || DOXYGEN //! \brief Installs a signal handler to execute `/system/bin/app_process` and diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index 22bd538c96..164cbe3008 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -465,6 +465,32 @@ bool CrashpadClient::StartHandler( return true; } +bool CrashpadClient::StartHandlerWithAttachments( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::map& fileAttachments, + const std::vector& arguments, + bool restartable, + bool asynchronous_start) { + std::vector updated_arguments = arguments; + for (const auto& kv: fileAttachments) { + std::string attachmentArg = "--attachment=" + kv.first + "=" + kv.second.value(); + updated_arguments.push_back(attachmentArg); + } + + return StartHandler(handler, + database, + metrics_dir, + url, + annotations, + updated_arguments, + restartable, + asynchronous_start); +} + bool CrashpadClient::SetHandlerMachService(const std::string& service_name) { base::mac::ScopedMachSendRight exception_port(BootstrapLookUp(service_name)); if (!exception_port.is_valid()) { diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 143dc4f1bb..106954db59 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -651,6 +651,33 @@ bool CrashpadClient::StartHandler( } } +bool CrashpadClient::StartHandlerWithAttachments( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::map& fileAttachments, + const std::vector& arguments, + bool restartable, + bool asynchronous_start) { + std::vector updated_arguments = arguments; + for (const auto& kv : fileAttachments) { + std::string attachmentArg = + "--attachment=" + kv.first + "=" + base::UTF16ToUTF8(kv.second.value()); + updated_arguments.push_back(attachmentArg); + } + + return StartHandler(handler, + database, + metrics_dir, + url, + annotations, + updated_arguments, + restartable, + asynchronous_start); +} + bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) { DCHECK(ipc_pipe_.empty()); DCHECK(!ipc_pipe.empty()); diff --git a/example.cpp b/example.cpp new file mode 100644 index 0000000000..474ae999bb --- /dev/null +++ b/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/handler/handler_main.cc b/handler/handler_main.cc index b223c9095e..dd8fc9dad6 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -149,9 +149,9 @@ void Usage(const base::FilePath& me) { #endif // OS_LINUX || OS_ANDROID " --url=URL send crash reports to this Breakpad server URL,\n" " only if uploads are enabled for the database\n" -#if defined(OS_WIN) || defined(OS_MACOSX) +#if !defined(OS_FUCHSIA) " --attachment=NAME=PATH attach a copy of a file, along with a crash dump\n" -#endif // OS_WIN || OS_MACOSX +#endif " --help display this help and exit\n" " --version output version information and exit\n", me.value().c_str()); @@ -581,9 +581,9 @@ int HandlerMain(int argc, kOptionSanitizationInformation, #endif kOptionURL, -#if defined(OS_WIN) || defined(OS_MACOSX) +#if !defined(OS_FUCHSIA) kOptionAttachment, -#endif // OS_WIN || OS_MACOSX +#endif // Standard options. kOptionHelp = -2, @@ -643,9 +643,9 @@ int HandlerMain(int argc, kOptionSanitizationInformation}, #endif // OS_LINUX || OS_ANDROID {"url", required_argument, nullptr, kOptionURL}, -#if defined(OS_WIN) || defined(OS_MACOSX) +#if !defined(OS_FUCHSIA) {"attachment", required_argument, nullptr, kOptionAttachment}, -#endif // OS_WIN || O_MACOSX +#endif {"help", no_argument, nullptr, kOptionHelp}, {"version", no_argument, nullptr, kOptionVersion}, {nullptr, 0, nullptr, 0}, @@ -783,14 +783,14 @@ int HandlerMain(int argc, options.url = optarg; break; } -#if defined(OS_WIN) || defined(OS_MACOSX) +#if !defined(OS_FUCHSIA) case kOptionAttachment: { if (!AddKeyValueToMap(&options.attachments, optarg, "--attachment")) { return ExitFailure(); } break; } -#endif // OS_WIN || OS_MACOSX +#endif case kOptionHelp: { Usage(me); MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly); @@ -917,7 +917,7 @@ int HandlerMain(int argc, #if defined(OS_FUCHSIA) // TODO(scottmg): Process level file attachments, and for all platforms. nullptr, -#elif defined(OS_WIN) || defined(OS_MACOSX) +#else &options.attachments, #endif user_stream_sources); From 6e7e91d167967f819a20e02bd8b98ecba4bb1a63 Mon Sep 17 00:00:00 2001 From: Anton Ovchinnikov Date: Fri, 22 Mar 2019 17:52:37 +0100 Subject: [PATCH 007/146] feat: Implement basic StartHandlerWithAttachments for Linux --- client/crashpad_client.h | 4 ++-- client/crashpad_client_linux.cc | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 7d91b59577..f60be213d4 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -112,7 +112,7 @@ class CrashpadClient { bool restartable, bool asynchronous_start); -#if defined(OS_WIN) || defined(OS_MACOSX) +#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) bool StartHandlerWithAttachments( const base::FilePath& handler, const base::FilePath& database, @@ -123,7 +123,7 @@ class CrashpadClient { const std::vector& arguments, bool restartable, bool asynchronous_start); -#endif // OS_WIN || OS_MACOSX +#endif // OS_WIN || OS_MACOSX || OS_LINUX #if defined(OS_ANDROID) || DOXYGEN //! \brief Installs a signal handler to execute `/system/bin/app_process` and diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index fe5798a534..37ea10eed6 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -238,6 +238,31 @@ bool CrashpadClient::StartHandler( return false; } +bool CrashpadClient::StartHandlerWithAttachments( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::map& fileAttachments, + const std::vector& arguments, + bool restartable, + bool asynchronous_start) { + std::vector updated_arguments = arguments; + for (const auto& kv: fileAttachments) { + std::string attachmentArg = "--attachment=" + kv.first + "=" + kv.second.value(); + updated_arguments.push_back(attachmentArg); + } + + // FIXME this is not the same as calling StartHandler on e.g. Mac + return CrashpadClient::StartHandlerAtCrash(handler, + database, + metrics_dir, + url, + annotations, + updated_arguments); +} + #if defined(OS_ANDROID) // static From 421c78e2663ae8e5501319a9975c281ea0cb5b1a Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Mon, 9 Dec 2019 13:59:46 +0100 Subject: [PATCH 008/146] Add throws declaration to memfd_create --- compat/linux/sys/mman.cc | 2 +- compat/linux/sys/mman.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compat/linux/sys/mman.cc b/compat/linux/sys/mman.cc index 12aaa2c7df..044dbcaee7 100644 --- a/compat/linux/sys/mman.cc +++ b/compat/linux/sys/mman.cc @@ -22,7 +22,7 @@ extern "C" { -int memfd_create(const char* name, unsigned int flags) { +int memfd_create(const char* name, unsigned int flags) __THROW { using MemfdCreateType = int (*)(const char*, int); static const MemfdCreateType next_memfd_create = reinterpret_cast(dlsym(RTLD_NEXT, "memfd_create")); diff --git a/compat/linux/sys/mman.h b/compat/linux/sys/mman.h index 61c55d7b95..43155da541 100644 --- a/compat/linux/sys/mman.h +++ b/compat/linux/sys/mman.h @@ -29,7 +29,7 @@ extern "C" { #endif -int memfd_create(const char* name, unsigned int flags); +int memfd_create(const char* name, unsigned int flags) __THROW; #ifdef __cplusplus } // extern "C" From 49414c4f67ba45f75071465d2228b802e6507735 Mon Sep 17 00:00:00 2001 From: Jan Michael Auer Date: Mon, 9 Dec 2019 14:28:47 +0100 Subject: [PATCH 009/146] Define __THROW in case it is undefined --- compat/linux/sys/mman.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compat/linux/sys/mman.h b/compat/linux/sys/mman.h index 43155da541..cc5e745e66 100644 --- a/compat/linux/sys/mman.h +++ b/compat/linux/sys/mman.h @@ -25,6 +25,10 @@ // which doesn't. #if defined(__GLIBC__) +#ifndef __THROW +#define __THROW +#endif + #ifdef __cplusplus extern "C" { #endif From 8a2ddfc0a475bff9e25bfc7af693bac373adebe3 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 21 Feb 2020 14:03:37 +0100 Subject: [PATCH 010/146] build: Add CMake files for crashpad (#2) * build: add CMake files for crashpad * add explicit submodule for mini_chromium * make cmake builds work on windows * review feedback, add separate getsentry README * better document changes, add update instructions --- .gitignore | 2 - .gitmodules | 6 + CMakeLists.txt | 78 +++++ Makefile | 15 +- README.getsentry.md | 54 ++++ README.md | 5 +- client/CMakeLists.txt | 58 ++++ compat/CMakeLists.txt | 89 ++++++ handler/CMakeLists.txt | 63 ++++ minidump/CMakeLists.txt | 65 ++++ snapshot/CMakeLists.txt | 181 ++++++++++++ third_party/getopt/CMakeLists.txt | 6 + third_party/mini_chromium/CMakeLists.txt | 136 +++++++++ third_party/mini_chromium/mini_chromium | 1 + third_party/zlib/CMakeLists.txt | 54 ++++ third_party/zlib/zlib | 1 + tools/CMakeLists.txt | 11 + util/CMakeLists.txt | 361 +++++++++++++++++++++++ 18 files changed, 1177 insertions(+), 9 deletions(-) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 README.getsentry.md create mode 100644 client/CMakeLists.txt create mode 100644 compat/CMakeLists.txt create mode 100644 handler/CMakeLists.txt create mode 100644 minidump/CMakeLists.txt create mode 100644 snapshot/CMakeLists.txt create mode 100644 third_party/getopt/CMakeLists.txt create mode 100644 third_party/mini_chromium/CMakeLists.txt create mode 160000 third_party/mini_chromium/mini_chromium create mode 100644 third_party/zlib/CMakeLists.txt create mode 160000 third_party/zlib/zlib create mode 100644 tools/CMakeLists.txt create mode 100644 util/CMakeLists.txt diff --git a/.gitignore b/.gitignore index 90038f1d21..6cfc15a8d1 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,5 @@ /third_party/linux/sysroot /third_party/lss/lss /third_party/gyp/gyp -/third_party/mini_chromium/mini_chromium -/third_party/zlib/zlib /xcodebuild tags diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..6b27a29859 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[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 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000..5b2d6c1dc3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,78 @@ +cmake_minimum_required(VERSION 3.0) +project(crashpad LANGUAGES C CXX) + +if(WIN32) + enable_language(ASM_MASM) +else() + enable_language(ASM) +endif() + +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINUX TRUE) +endif() + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}") +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/third_party/mini_chromium/mini_chromium") + +# These should really be in `compat`, but we want them for the whole project +if(APPLE) + include_directories(SYSTEM "compat/mac") +else() + include_directories(PUBLIC "compat/non_mac") +endif() + +if(LINUX OR ANDROID) + include_directories(SYSTEM "compat/linux") +endif() +if(ANDROID) + include_directories(SYSTEM "compat/android") +endif() + +if(WIN32) + include_directories(SYSTEM "compat/win") +else() + include_directories(PUBLIC "compat/non_win") +endif() + +if(NOT LINUX AND NOT ANDROID) + include_directories(SYSTEM "compat/non_elf") +endif() + +if(WIN32) + add_definitions( + /DNOMINMAX + /DUNICODE + /DWIN32_LEAN_AND_MEAN + /D_CRT_SECURE_NO_WARNINGS + /D_HAS_EXCEPTIONS=0 + /D_UNICODE + /FS + /W4 + /WX + /Zi + /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. + ) +endif() + +add_subdirectory(client) +add_subdirectory(compat) +add_subdirectory(handler) +add_subdirectory(minidump) +add_subdirectory(snapshot) +add_subdirectory(tools) +add_subdirectory(util) +add_subdirectory(third_party/mini_chromium) + +if(WIN32) + add_subdirectory(third_party/getopt) + add_subdirectory(third_party/zlib) +endif() diff --git a/Makefile b/Makefile index 80a41c2b2c..6d88f6fc44 100644 --- a/Makefile +++ b/Makefile @@ -4,15 +4,22 @@ PATH := $(PWD)/../depot_tools:$(PATH) all: echo 'Nothing to do' && exit 1 -build: +build-with-gn: gn gen out/Default ninja -C out/Default -.PHONY: build +.PHONY: build-with-gn -update: +build-with-cmake: + mkdir -p cmakebuild + cd cmakebuild; cmake .. + cmake --build cmakebuild --parallel +.PHONY: build-with-cmake + +update-with-gclient: gclient sync +.PHONY: update-with-gclient -example: +example: build-with-gn g++ -g \ -o example example.cpp \ -I. -I./third_party/mini_chromium/mini_chromium \ diff --git a/README.getsentry.md b/README.getsentry.md new file mode 100644 index 0000000000..a0af84edb4 --- /dev/null +++ b/README.getsentry.md @@ -0,0 +1,54 @@ +# 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 + +# 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/lib/CMakeLists.txt` +- `./tools/CMakeLists.txt` +- `./util/CMakeLists.txt` + +The important thing here is to keep the list of source files in sync when +updating. + +# 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/README.md b/README.md index 1da5384fd5..06a5d046ec 100644 --- a/README.md +++ b/README.md @@ -43,9 +43,8 @@ https://chromium.googlesource.com/crashpad/crashpad. ## 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. +See [README.getsentry.md](README.getsentry.md) for more information on the +changes, and on maintaining the fork. Generating patch: diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 0000000000..940e268823 --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,58 @@ +list(APPEND CLIENT_SOURCES + 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) + list(APPEND CLIENT_SOURCES + crash_report_database_mac.mm + crashpad_client_mac.cc + simulate_crash_mac.cc + simulate_crash_mac.h + ) +endif() + +if(LINUX OR ANDROID) + list(APPEND CLIENT_SOURCES + crashpad_client_linux.cc + simulate_crash_linux.h + client_argv_handling.cc + client_argv_handling.h + crashpad_info_note.S + crash_report_database_generic.cc + ) +endif() + +if(WIN32) + list(APPEND CLIENT_SOURCES + crash_report_database_win.cc + crashpad_client_win.cc + simulate_crash_win.h + ) +endif() + +add_library(crashpad_client STATIC ${CLIENT_SOURCES}) +target_link_libraries(crashpad_client + crashpad_compat + crashpad_util + mini_chromium +) + +if(WIN32) + target_link_libraries(crashpad_client "rpcrt4") + target_compile_options(crashpad_client PUBLIC "/wd4201") +endif() diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt new file mode 100644 index 0000000000..576bde10f3 --- /dev/null +++ b/compat/CMakeLists.txt @@ -0,0 +1,89 @@ +list(APPEND COMPAT_SOURCES "") + +if(APPLE) + list(APPEND COMPAT_SOURCES + mac/AvailabilityMacros.h + mac/kern/exc_resource.h + mac/mach-o/loader.h + mac/mach/mach.h + mac/sys/resource.h + ) +else() + list(APPEND COMPAT_SOURCES + non_mac/mach-o/loader.h + non_mac/mach/mach.h + non_mac/mach/machine.h + non_mac/mach/vm_prot.h + ) +endif() + +if(LINUX OR ANDROID) + list(APPEND COMPAT_SOURCES + linux/signal.h + linux/sys/mman.cc + linux/sys/mman.h + linux/sys/ptrace.h + linux/sys/user.h + ) +endif() + +if(ANDROID) + list(APPEND COMPAT_SOURCES + android/android/api-level.cc + android/android/api-level.h + 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.cc + android/sys/mman.h + android/sys/syscall.h + android/sys/user.h + ) +endif() + +if(WIN32) + 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 + ) +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(NOT LINUX AND NOT ANDROID) + list(APPEND COMPAT_SOURCES + non_elf/elf.h + ) +endif() + +if(APPLE) + add_library(crashpad_compat INTERFACE) + list(TRANSFORM COMPAT_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") + target_sources(crashpad_compat INTERFACE ${COMPAT_SOURCES}) +else() + add_library(crashpad_compat STATIC ${COMPAT_SOURCES}) +endif() + +if(WIN32) + target_link_libraries(crashpad_compat getopt) +endif() diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt new file mode 100644 index 0000000000..b751a2ee09 --- /dev/null +++ b/handler/CMakeLists.txt @@ -0,0 +1,63 @@ +list(APPEND HANDLER_SOURCES + main.cc + 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) + list(APPEND HANDLER_SOURCES + mac/crash_report_exception_handler.cc + mac/crash_report_exception_handler.h + mac/exception_handler_server.cc + mac/exception_handler_server.h + mac/file_limit_annotation.cc + mac/file_limit_annotation.h + ) +endif() + +if(LINUX OR ANDROID) + list(APPEND HANDLER_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 + ) +endif() + +if(LINUX) + list(APPEND HANDLER_SOURCES + linux/cros_crash_report_exception_handler.cc + linux/cros_crash_report_exception_handler.h + ) +endif() + +if(WIN32) + list(APPEND HANDLER_SOURCES + win/crash_report_exception_handler.cc + win/crash_report_exception_handler.h + ) +endif() + +add_executable(crashpad_handler ${HANDLER_SOURCES}) +target_link_libraries(crashpad_handler + crashpad_client + crashpad_minidump + crashpad_snapshot + crashpad_tools + crashpad_util + mini_chromium +) + +if(WIN32) + target_compile_options(crashpad_handler PUBLIC "/wd4201") +endif() diff --git a/minidump/CMakeLists.txt b/minidump/CMakeLists.txt new file mode 100644 index 0000000000..75ab21d57c --- /dev/null +++ b/minidump/CMakeLists.txt @@ -0,0 +1,65 @@ +list(APPEND MINIDUMP_SOURCES + 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 +) + +add_library(crashpad_minidump STATIC ${MINIDUMP_SOURCES}) +target_link_libraries(crashpad_minidump + crashpad_compat + crashpad_snapshot + crashpad_util + mini_chromium +) + +if(WIN32) + target_compile_options(crashpad_minidump PUBLIC "/wd4201" "/wd4324") +endif() diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt new file mode 100644 index 0000000000..f349514b86 --- /dev/null +++ b/snapshot/CMakeLists.txt @@ -0,0 +1,181 @@ +list(APPEND SNAPSHOT_SOURCES + 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) + list(APPEND SNAPSHOT_SOURCES + 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 + ) +else() + list(APPEND SNAPSHOT_SOURCES + crashpad_types/crashpad_info_reader.cc + crashpad_types/crashpad_info_reader.h + ) +endif() + +if(LINUX OR ANDROID) + list(APPEND SNAPSHOT_SOURCES + posix/timezone.cc + posix/timezone.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) + list(APPEND SNAPSHOT_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 + ) +endif() + +# TODO: if(x86) + list(APPEND SNAPSHOT_SOURCES + x86/cpuid_reader.cc + x86/cpuid_reader.h + ) +#endif() + +add_library(crashpad_snapshot STATIC ${SNAPSHOT_SOURCES}) +target_link_libraries(crashpad_snapshot + crashpad_client + crashpad_compat + crashpad_util + mini_chromium +) + +if(WIN32) + target_link_libraries(crashpad_snapshot "powrprof") + target_compile_options(crashpad_snapshot PUBLIC "/wd4201") +endif() diff --git a/third_party/getopt/CMakeLists.txt b/third_party/getopt/CMakeLists.txt new file mode 100644 index 0000000000..7671ca0416 --- /dev/null +++ b/third_party/getopt/CMakeLists.txt @@ -0,0 +1,6 @@ +list(APPEND GETOPT_SOURCES + getopt.cc + getopt.h +) + +add_library(getopt STATIC ${GETOPT_SOURCES}) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt new file mode 100644 index 0000000000..a2fb8d5381 --- /dev/null +++ b/third_party/mini_chromium/CMakeLists.txt @@ -0,0 +1,136 @@ +list(APPEND MINI_CHROMIUM_SOURCES + ../build/build_config.h + atomicops.h + atomicops_internals_atomicword_compat.h + atomicops_internals_portable.h + auto_reset.h + bit_cast.h + compiler_specific.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 + macros.h + memory/free_deleter.h + memory/scoped_policy.h + metrics/histogram_functions.h + metrics/histogram_macros.h + metrics/persistent_histogram_allocator.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 + process/process_metrics.h + rand_util.cc + rand_util.h + scoped_clear_errno.h + scoped_generic.h + stl_util.h + strings/string16.cc + strings/string16.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(APPLE) + list(APPEND MINI_CHROMIUM_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 + ) +endif() + +if(WIN32) + list(APPEND MINI_CHROMIUM_SOURCES + process/process_metrics_win.cc + strings/string_util_win.cc + strings/string_util_win.h + synchronization/lock_impl_win.cc + threading/thread_local_storage_win.cc + ) +else() + list(APPEND MINI_CHROMIUM_SOURCES + files/file_util_posix.cc + posix/eintr_wrapper.h + posix/safe_strerror.cc + posix/safe_strerror.h + process/process_metrics_posix.cc + strings/string_util_posix.h + synchronization/condition_variable_posix.cc + synchronization/lock_impl_posix.cc + threading/thread_local_storage_posix.cc + ) +endif() + +list(TRANSFORM MINI_CHROMIUM_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/mini_chromium/base/") + +add_library(mini_chromium STATIC ${MINI_CHROMIUM_SOURCES}) + +if(APPLE) + target_link_libraries(mini_chromium PUBLIC "-framework ApplicationServices") + target_link_libraries(mini_chromium PUBLIC "-framework CoreFoundation") + target_link_libraries(mini_chromium PUBLIC "-framework Foundation") + target_link_libraries(mini_chromium PUBLIC "-framework IOKit") + target_link_libraries(mini_chromium PUBLIC "-framework Security") +endif() + +if(WIN32) + target_link_libraries(mini_chromium "advapi32" "kernel32") + target_compile_options(mini_chromium PUBLIC "/wd4201" "/wd4996") + target_compile_definitions(mini_chromium PUBLIC + NOMINMAX + UNICODE + WIN32_LEAN_AND_MEAN + _CRT_SECURE_NO_WARNINGS + _HAS_EXCEPTIONS=0 + _UNICODE + ) +endif() diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium new file mode 160000 index 0000000000..f8f1182adb --- /dev/null +++ b/third_party/mini_chromium/mini_chromium @@ -0,0 +1 @@ +Subproject commit f8f1182adb804675b2aa4fb3ce03f6f884fae474 diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt new file mode 100644 index 0000000000..d36dac5cf5 --- /dev/null +++ b/third_party/zlib/CMakeLists.txt @@ -0,0 +1,54 @@ +list(APPEND ZLIB_SOURCES + zlib/adler32.c + zlib/compress.c + zlib/crc32.c + zlib/crc32.h + zlib/deflate.c + zlib/deflate.h + zlib/gzclose.c + zlib/gzguts.h + zlib/gzlib.c + zlib/gzread.c + zlib/gzwrite.c + zlib/infback.c + zlib/inffast.c + zlib/inffast.h + zlib/inffixed.h + zlib/inflate.c + zlib/inflate.h + zlib/inftrees.c + zlib/inftrees.h + zlib/names.h + zlib/trees.c + zlib/trees.h + zlib/uncompr.c + zlib/zconf.h + zlib/zlib.h + zlib/zutil.c + zlib/zutil.h + zlib_crashpad.h +) + +# TODO: x86/x64 +list(APPEND ZLIB_SOURCES + zlib/crc_folding.c + zlib/fill_window_sse.c + zlib/x86.c + zlib/x86.h +) + +add_library(zlib STATIC ${ZLIB_SOURCES}) +target_compile_definitions(zlib PUBLIC HAVE_STDARG_H) + +include_directories(zlib) + +if(WIN32) + target_compile_options(zlib PUBLIC + "/wd4131" # uses old-style declarator + "/wd4244" # conversion from 't1' to 't2', possible loss of data + "/wd4245" # conversion from 't1' to 't2', signed/unsigned mismatch + "/wd4267" # conversion from 'size_t' to 't', possible loss of data + "/wd4324" # structure was padded due to alignment specifier + "/wd4702" # unreachable code + ) +endif() diff --git a/third_party/zlib/zlib b/third_party/zlib/zlib new file mode 160000 index 0000000000..13dc246a58 --- /dev/null +++ b/third_party/zlib/zlib @@ -0,0 +1 @@ +Subproject commit 13dc246a58e4b72104d35f9b1809af95221ebda7 diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000000..f72f7e6e81 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,11 @@ +list(APPEND TOOLS_SOURCES + tool_support.cc + tool_support.h +) + +add_library(crashpad_tools STATIC ${TOOLS_SOURCES}) +target_link_libraries(crashpad_tools + mini_chromium +) + +# TODO: do we need any tool executables? diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt new file mode 100644 index 0000000000..95e70c227c --- /dev/null +++ b/util/CMakeLists.txt @@ -0,0 +1,361 @@ +list(APPEND UTIL_SOURCES + file/delimited_file_reader.cc + file/delimited_file_reader.h + file/directory_reader.h + file/file_io.cc + file/file_io.h + file/file_reader.cc + file/file_reader.h + file/file_seeker.cc + file/file_seeker.h + file/file_writer.cc + file/file_writer.h + file/filesystem.h + file/scoped_remove_file.cc + file/scoped_remove_file.h + file/string_file.cc + file/string_file.h + misc/address_sanitizer.h + misc/address_types.h + misc/arraysize.h + misc/as_underlying_type.h + misc/capture_context.h + misc/clock.h + misc/elf_note_types.h + misc/from_pointer_cast.h + misc/implicit_cast.h + misc/initialization_state.h + misc/initialization_state_dcheck.cc + misc/initialization_state_dcheck.h + misc/lexing.cc + misc/lexing.h + misc/memory_sanitizer.h + misc/metrics.cc + misc/metrics.h + misc/paths.h + misc/pdb_structures.cc + misc/pdb_structures.h + misc/random_string.cc + misc/random_string.h + misc/range_set.cc + misc/range_set.h + misc/reinterpret_bytes.cc + misc/reinterpret_bytes.h + misc/scoped_forbid_return.cc + misc/scoped_forbid_return.h + misc/symbolic_constants_common.h + misc/time.cc + misc/time.h + misc/tri_state.h + misc/uuid.cc + misc/uuid.h + misc/zlib.cc + misc/zlib.h + net/http_body.cc + net/http_body.h + net/http_body_gzip.cc + net/http_body_gzip.h + net/http_headers.h + net/http_multipart_builder.cc + net/http_multipart_builder.h + net/http_transport.cc + net/http_transport.h + net/url.cc + net/url.h + numeric/checked_address_range.cc + numeric/checked_address_range.h + numeric/checked_range.h + numeric/checked_vm_address_range.h + numeric/in_range_cast.h + numeric/int128.h + numeric/safe_assignment.h + process/process_id.h + process/process_memory.cc + process/process_memory.h + process/process_memory_native.h + process/process_memory_range.cc + process/process_memory_range.h + stdlib/aligned_allocator.cc + stdlib/aligned_allocator.h + stdlib/map_insert.h + stdlib/objc.h + stdlib/string_number_conversion.cc + stdlib/string_number_conversion.h + stdlib/strlcpy.cc + stdlib/strlcpy.h + stdlib/strnlen.cc + stdlib/strnlen.h + stdlib/thread_safe_vector.h + stream/output_stream_interface.h + stream/zlib_output_stream.cc + stream/zlib_output_stream.h + string/split_string.cc + string/split_string.h + synchronization/semaphore.h + thread/stoppable.h + thread/thread.cc + thread/thread.h + thread/thread_log_messages.cc + thread/thread_log_messages.h + thread/worker_thread.cc + thread/worker_thread.h +) + +if(NOT WIN32) + list(APPEND UTIL_SOURCES + file/directory_reader_posix.cc + file/file_io_posix.cc + file/filesystem_posix.cc + misc/clock_posix.cc + posix/close_stdio.cc + posix/close_stdio.h + posix/scoped_dir.cc + posix/scoped_dir.h + posix/scoped_mmap.cc + posix/scoped_mmap.h + posix/signals.cc + posix/signals.h + synchronization/semaphore_posix.cc + thread/thread_posix.cc + posix/close_multiple.cc + posix/close_multiple.h + posix/double_fork_and_exec.cc + posix/double_fork_and_exec.h + posix/drop_privileges.cc + posix/drop_privileges.h + posix/process_info.h + posix/symbolic_constants_posix.cc + posix/symbolic_constants_posix.h + ) +endif() + +if(APPLE) + list(APPEND UTIL_SOURCES + mac/checked_mach_address_range.h + mac/launchd.h + mac/launchd.mm + mac/mac_util.cc + mac/mac_util.h + mac/service_management.cc + mac/service_management.h + mac/xattr.cc + mac/xattr.h + mach/child_port_handshake.cc + mach/child_port_handshake.h + mach/child_port_server.cc + mach/child_port_server.h + mach/child_port_types.h + mach/composite_mach_message_server.cc + mach/composite_mach_message_server.h + mach/exc_client_variants.cc + mach/exc_client_variants.h + mach/exc_server_variants.cc + mach/exc_server_variants.h + mach/exception_behaviors.cc + mach/exception_behaviors.h + mach/exception_ports.cc + mach/exception_ports.h + mach/exception_types.cc + mach/exception_types.h + mach/mach_extensions.cc + mach/mach_extensions.h + mach/mach_message.cc + mach/mach_message.h + mach/mach_message_server.cc + mach/mach_message_server.h + mach/notify_server.cc + mach/notify_server.h + mach/scoped_task_suspend.cc + mach/scoped_task_suspend.h + mach/symbolic_constants_mach.cc + mach/symbolic_constants_mach.h + mach/task_for_pid.cc + mach/task_for_pid.h + misc/capture_context_mac.S + misc/clock_mac.cc + misc/paths_mac.cc + net/http_transport_mac.mm + posix/process_info_mac.cc + process/process_memory_mac.cc + process/process_memory_mac.h + synchronization/semaphore_mac.cc + ) +endif() + +if(LINUX OR ANDROID) + list(APPEND UTIL_SOURCES + net/http_transport_socket.cc + linux/address_types.h + linux/auxiliary_vector.cc + linux/auxiliary_vector.h + linux/checked_linux_address_range.h + linux/direct_ptrace_connection.cc + linux/direct_ptrace_connection.h + linux/exception_handler_client.cc + linux/exception_handler_client.h + linux/exception_handler_protocol.cc + linux/exception_handler_protocol.h + linux/exception_information.h + linux/memory_map.cc + linux/memory_map.h + linux/proc_stat_reader.cc + linux/proc_stat_reader.h + linux/proc_task_reader.cc + linux/proc_task_reader.h + linux/ptrace_broker.cc + linux/ptrace_broker.h + linux/ptrace_client.cc + linux/ptrace_client.h + linux/ptrace_connection.h + linux/ptracer.cc + linux/ptracer.h + linux/scoped_pr_set_dumpable.cc + linux/scoped_pr_set_dumpable.h + linux/scoped_pr_set_ptracer.cc + linux/scoped_pr_set_ptracer.h + linux/scoped_ptrace_attach.cc + linux/scoped_ptrace_attach.h + linux/socket.cc + linux/socket.h + linux/thread_info.cc + linux/thread_info.h + linux/traits.h + misc/capture_context_linux.S + misc/paths_linux.cc + posix/process_info_linux.cc + process/process_memory_linux.cc + process/process_memory_linux.h + process/process_memory_sanitized.cc + process/process_memory_sanitized.h + ) +endif() + +if(WIN32) + list(APPEND UTIL_SOURCES + file/directory_reader_win.cc + file/file_io_win.cc + file/filesystem_win.cc + misc/clock_win.cc + misc/paths_win.cc + misc/time_win.cc + net/http_transport_win.cc + process/process_memory_win.cc + process/process_memory_win.h + synchronization/semaphore_win.cc + thread/thread_win.cc + win/address_types.h + win/checked_win_address_range.h + win/command_line.cc + win/command_line.h + win/context_wrappers.h + win/critical_section_with_debug_info.cc + win/critical_section_with_debug_info.h + win/exception_handler_server.cc + win/exception_handler_server.h + win/get_function.cc + win/get_function.h + win/get_module_information.cc + win/get_module_information.h + win/handle.cc + win/handle.h + win/initial_client_data.cc + win/initial_client_data.h + win/module_version.cc + win/module_version.h + win/nt_internals.cc + win/nt_internals.h + win/ntstatus_logging.cc + win/ntstatus_logging.h + win/process_info.cc + win/process_info.h + win/process_structs.h + win/registration_protocol_win.cc + win/registration_protocol_win.h + win/safe_terminate_process.h + win/scoped_handle.cc + win/scoped_handle.h + win/scoped_local_alloc.cc + win/scoped_local_alloc.h + win/scoped_process_suspend.cc + win/scoped_process_suspend.h + win/scoped_set_event.cc + win/scoped_set_event.h + win/session_end_watcher.cc + win/session_end_watcher.h + win/termination_codes.h + win/xp_compat.h + + misc/capture_context_win.asm + win/safe_terminate_process.asm + ) +endif() + +# Copied from: https://github.com/qedsoftware/crashpad/blob/3583c50a6575857abcf140f6ea3b8d11390205b3/util/CMakeLists.txt#L196-L233 +if(APPLE) + set(def_relative_files "exc.defs" "mach_exc.defs" "notify.defs") + set(input_files "") + foreach(x ${def_relative_files}) + # CMAKE_OSX_SYSROOT may be empty (e.g. for Makefile generators), + # in this case files will be taken from root. + set(full_path "${CMAKE_OSX_SYSROOT}/usr/include/mach/${x}") + if(NOT EXISTS "${full_path}") + message(FATAL_ERROR "File not found: ${full_path}") + endif() + list(APPEND input_files "${full_path}") + endforeach() + list(APPEND input_files "${CMAKE_CURRENT_LIST_DIR}/mach/child_port.defs") + + find_package(PythonInterp 2.7 REQUIRED) + + set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/util/mach") + file(MAKE_DIRECTORY "${output_dir}") + + # Create generate rule for each input file. Add each generated output + # as a source to the target. + foreach(input ${input_files}) + get_filename_component(name_we "${input}" NAME_WE) + set(output_files "") + foreach(suffix "User.c" "Server.c" ".h" "Server.h") + list(APPEND output_files "${output_dir}/${name_we}${suffix}") + endforeach() + add_custom_command( + OUTPUT + ${output_files} + COMMAND + "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" ${output_files} + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" + ) + list(APPEND UTIL_SOURCES ${output_files}) + endforeach() + + include_directories("${CMAKE_CURRENT_BINARY_DIR}") +endif() + +add_library(crashpad_util STATIC ${UTIL_SOURCES}) +target_link_libraries(crashpad_util PUBLIC + crashpad_compat + mini_chromium +) + +target_compile_definitions(crashpad_util PUBLIC "-DZLIB_CONST") + +if(WIN32) + target_compile_definitions(crashpad_util PUBLIC "-DCRASHPAD_ZLIB_SOURCE_EMBEDDED") + target_link_libraries(crashpad_util PUBLIC zlib) +else() + target_compile_definitions(crashpad_util PUBLIC "-DCRASHPAD_ZLIB_SOURCE_SYSTEM") + target_link_libraries(crashpad_util PUBLIC "z") +endif() + +if(APPLE) + target_link_libraries(crashpad_util PUBLIC "bsm") + target_link_libraries(crashpad_util PUBLIC "-framework CoreFoundation") + target_link_libraries(crashpad_util PUBLIC "-framework Foundation") + target_link_libraries(crashpad_util PUBLIC "-framework IOKit") +endif() + +if(WIN32) + target_link_libraries(crashpad_util PUBLIC "user32" "version" "winhttp") + target_compile_options(crashpad_util PUBLIC "/wd4201") +endif() From e2dbaa7918b30301c19c3c2e4c37252e79751483 Mon Sep 17 00:00:00 2001 From: Mikhail Paulyshka Date: Fri, 20 Mar 2020 18:09:37 +0300 Subject: [PATCH 011/146] handler: set Windows subsystem to hide console window (#4) --- handler/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index b751a2ee09..abac15b14f 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -48,7 +48,11 @@ if(WIN32) ) endif() -add_executable(crashpad_handler ${HANDLER_SOURCES}) +if(WIN32) + add_executable(crashpad_handler WIN32 ${HANDLER_SOURCES}) +else() + add_executable(crashpad_handler ${HANDLER_SOURCES}) +endif() target_link_libraries(crashpad_handler crashpad_client crashpad_minidump From 5e7b7b85620c82ed3a19ca34098ad59d910b5095 Mon Sep 17 00:00:00 2001 From: Amphaal Date: Sat, 21 Mar 2020 17:31:45 +0100 Subject: [PATCH 012/146] Enable MinGW builds (#5) --- CMakeLists.txt | 22 +- client/CMakeLists.txt | 19 +- compat/CMakeLists.txt | 10 +- compat/mingw/dbghelp.h | 249 ++++++++++++++++++ compat/mingw/winnt.h | 60 +++++ handler/CMakeLists.txt | 12 +- minidump/CMakeLists.txt | 8 +- snapshot/CMakeLists.txt | 14 +- third_party/mini_chromium/CMakeLists.txt | 37 ++- .../utf_string_conversion_utils.mingw.cc | 105 ++++++++ third_party/zlib/CMakeLists.txt | 2 +- util/CMakeLists.txt | 20 +- util/misc/capture_context_win.asm | 5 +- util/stdlib/aligned_allocator.cc | 4 +- util/win/process_info.cc | 4 +- 15 files changed, 542 insertions(+), 29 deletions(-) create mode 100644 compat/mingw/dbghelp.h create mode 100644 compat/mingw/winnt.h create mode 100644 third_party/mini_chromium/utf_string_conversion_utils.mingw.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b2d6c1dc3..371960d5d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,15 @@ project(crashpad LANGUAGES C CXX) if(WIN32) enable_language(ASM_MASM) + IF(MINGW) + find_program(UASM uasm) + if(UASM) + SET(CMAKE_ASM_MASM_COMPILER "uasm") + SET(CMAKE_ASM_MASM_FLAGS "-win64 -10") + else() + message(FATAL_ERROR "UASM is required for MinGW builds ! Make sure you have it installed") + endif() + endif() else() enable_language(ASM) endif() @@ -31,8 +40,10 @@ if(ANDROID) include_directories(SYSTEM "compat/android") endif() -if(WIN32) +if(MSVC) include_directories(SYSTEM "compat/win") +elseif(MINGW) + include_directories(PUBLIC "compat/mingw") else() include_directories(PUBLIC "compat/non_win") endif() @@ -41,7 +52,7 @@ if(NOT LINUX AND NOT ANDROID) include_directories(SYSTEM "compat/non_elf") endif() -if(WIN32) +if(MSVC) add_definitions( /DNOMINMAX /DUNICODE @@ -61,6 +72,11 @@ if(WIN32) /wd4577 # 'noexcept' used with no exception handling mode specified. /wd4996 # 'X' was declared deprecated. ) +elseif(MINGW) + #redirect to wmain + add_link_options( + -municode + ) endif() add_subdirectory(client) @@ -72,7 +88,7 @@ add_subdirectory(tools) add_subdirectory(util) add_subdirectory(third_party/mini_chromium) -if(WIN32) +if(MSVC) add_subdirectory(third_party/getopt) add_subdirectory(third_party/zlib) endif() diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 940e268823..77787c13be 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -54,5 +54,22 @@ target_link_libraries(crashpad_client if(WIN32) target_link_libraries(crashpad_client "rpcrt4") - target_compile_options(crashpad_client PUBLIC "/wd4201") + if(MSVC) + target_compile_options(crashpad_client PUBLIC "/wd4201") + elseif(MINGW) + target_compile_options(crashpad_client PRIVATE + "-municode" + ) + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_client PRIVATE + "-Wno-multichar" + "-Wno-attributes" + ) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(crashpad_client PRIVATE + "-Wno-unknown-attributes" + "-Wno-unknown-pragmas" + ) + endif() endif() diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt index 576bde10f3..ccf3d562cc 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -47,7 +47,7 @@ if(ANDROID) ) endif() -if(WIN32) +if(MSVC) list(APPEND COMPAT_SOURCES win/getopt.h win/strings.cc @@ -59,6 +59,11 @@ if(WIN32) 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 @@ -82,8 +87,9 @@ if(APPLE) target_sources(crashpad_compat INTERFACE ${COMPAT_SOURCES}) else() add_library(crashpad_compat STATIC ${COMPAT_SOURCES}) + set_target_properties(crashpad_compat PROPERTIES LINKER_LANGUAGE CXX) endif() -if(WIN32) +if(MSVC) target_link_libraries(crashpad_compat getopt) endif() diff --git a/compat/mingw/dbghelp.h b/compat/mingw/dbghelp.h new file mode 100644 index 0000000000..45efe1920c --- /dev/null +++ b/compat/mingw/dbghelp.h @@ -0,0 +1,249 @@ +// 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_ + +#include_next + +#include + +#include "base/strings/string16.h" +#include +#include +#include + +//! \file + +//! \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 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. +//! +//! 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)”. + base::char16 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. + base::char16 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; + +#endif // CRASHPAD_COMPAT_MINGW_DBGHELP_H_ diff --git a/compat/mingw/winnt.h b/compat/mingw/winnt.h new file mode 100644 index 0000000000..268af1b3bc --- /dev/null +++ b/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/handler/CMakeLists.txt b/handler/CMakeLists.txt index abac15b14f..8b76bd39d2 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -62,6 +62,14 @@ target_link_libraries(crashpad_handler mini_chromium ) -if(WIN32) - target_compile_options(crashpad_handler PUBLIC "/wd4201") +if(WIN32) + if(MSVC) + target_compile_options(crashpad_handler PUBLIC "/wd4201") + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_handler PRIVATE + "-Wno-multichar" + ) + endif() endif() + diff --git a/minidump/CMakeLists.txt b/minidump/CMakeLists.txt index 75ab21d57c..0a2e0dda13 100644 --- a/minidump/CMakeLists.txt +++ b/minidump/CMakeLists.txt @@ -60,6 +60,12 @@ target_link_libraries(crashpad_minidump mini_chromium ) -if(WIN32) +if(MSVC) target_compile_options(crashpad_minidump PUBLIC "/wd4201" "/wd4324") endif() + +if(WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_minidump PRIVATE + "-Wno-multichar" + ) +endif() \ No newline at end of file diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index f349514b86..698997c1cd 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -177,5 +177,17 @@ target_link_libraries(crashpad_snapshot if(WIN32) target_link_libraries(crashpad_snapshot "powrprof") - target_compile_options(crashpad_snapshot PUBLIC "/wd4201") + if(MSVC) + target_compile_options(crashpad_snapshot PUBLIC "/wd4201") + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_snapshot PRIVATE + "-Wno-multichar" + "-Wno-attributes" + ) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(crashpad_snapshot PRIVATE + "-Wno-unknown-attributes" + ) + endif() endif() diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index a2fb8d5381..c3a3245519 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -50,7 +50,6 @@ list(APPEND MINI_CHROMIUM_SOURCES 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 @@ -66,6 +65,16 @@ list(APPEND MINI_CHROMIUM_SOURCES threading/thread_local_storage.h ) +if(NOT MINGW) + list(APPEND MINI_CHROMIUM_SOURCES + strings/utf_string_conversion_utils.cc + ) +else() + list(APPEND MINI_CHROMIUM_SOURCES + ../../utf_string_conversion_utils.mingw.cc + ) +endif() + if(APPLE) list(APPEND MINI_CHROMIUM_SOURCES mac/close_nocancel.cc @@ -124,13 +133,21 @@ endif() if(WIN32) target_link_libraries(mini_chromium "advapi32" "kernel32") - target_compile_options(mini_chromium PUBLIC "/wd4201" "/wd4996") - target_compile_definitions(mini_chromium PUBLIC - NOMINMAX - UNICODE - WIN32_LEAN_AND_MEAN - _CRT_SECURE_NO_WARNINGS - _HAS_EXCEPTIONS=0 - _UNICODE - ) + if(MSVC) + target_compile_options(mini_chromium PUBLIC "/wd4201" "/wd4996") + target_compile_definitions(mini_chromium PUBLIC + 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() diff --git a/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc b/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc new file mode 100644 index 0000000000..6baf7b452d --- /dev/null +++ b/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc @@ -0,0 +1,105 @@ +// 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. + +#include "base/strings/utf_string_conversion_utils.h" + +#include "base/third_party/icu/icu_utf.h" + +namespace base { + +bool ReadUnicodeCharacter(const char* src, + int32_t src_len, + int32_t* char_index, + uint32_t* code_point_out) { + int32_t code_point; + CBU8_NEXT(src, *char_index, src_len, code_point); + *code_point_out = static_cast(code_point); + + (*char_index)--; + + return IsValidCodepoint(code_point); +} + +bool ReadUnicodeCharacter(const char16* src, + int32_t src_len, + int32_t* char_index, + uint32_t* code_point) { + if (CBU16_IS_SURROGATE(src[*char_index])) { + if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) || + *char_index + 1 >= src_len || + !CBU16_IS_TRAIL(src[*char_index + 1])) { + return false; + } + + *code_point = CBU16_GET_SUPPLEMENTARY(src[*char_index], + src[*char_index + 1]); + (*char_index)++; + } else { + *code_point = src[*char_index]; + } + + return IsValidCodepoint(*code_point); +} + +size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output) { + if (code_point <= 0x7f) { + output->push_back(static_cast(code_point)); + return 1; + } + + size_t char_offset = output->length(); + size_t original_char_offset = char_offset; + output->resize(char_offset + CBU8_MAX_LENGTH); + + CBU8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point); + + output->resize(char_offset); + return char_offset - original_char_offset; +} + +size_t WriteUnicodeCharacter(uint32_t code_point, string16* output) { + if (CBU16_LENGTH(code_point) == 1) { + output->push_back(static_cast(code_point)); + return 1; + } + size_t char_offset = output->length(); + output->resize(char_offset + CBU16_MAX_LENGTH); + CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point); + return CBU16_MAX_LENGTH; +} + +template +void PrepareForUTF8Output(const CHAR* src, + size_t src_len, + std::string* output) { + output->clear(); + if (src_len == 0) + return; + if (src[0] < 0x80) { + output->reserve(src_len); + } else { + output->reserve(src_len * 3); + } +} + +template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*); +// template void PrepareForUTF8Output(const char16*, size_t, std::string*); + +template +void PrepareForUTF16Or32Output(const char* src, + size_t src_len, + STRING* output) { + output->clear(); + if (src_len == 0) + return; + if (static_cast(src[0]) < 0x80) { + output->reserve(src_len); + } else { + output->reserve(src_len / 2); + } +} + +template void PrepareForUTF16Or32Output(const char*, size_t, string16*); + +} // namespace base diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index d36dac5cf5..562452c9d9 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -42,7 +42,7 @@ target_compile_definitions(zlib PUBLIC HAVE_STDARG_H) include_directories(zlib) -if(WIN32) +if(MSVC) target_compile_options(zlib PUBLIC "/wd4131" # uses old-style declarator "/wd4244" # conversion from 't1' to 't2', possible loss of data diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 95e70c227c..3a796b8a67 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -284,7 +284,6 @@ if(WIN32) win/session_end_watcher.h win/termination_codes.h win/xp_compat.h - misc/capture_context_win.asm win/safe_terminate_process.asm ) @@ -340,7 +339,7 @@ target_link_libraries(crashpad_util PUBLIC target_compile_definitions(crashpad_util PUBLIC "-DZLIB_CONST") -if(WIN32) +if(MSVC) target_compile_definitions(crashpad_util PUBLIC "-DCRASHPAD_ZLIB_SOURCE_EMBEDDED") target_link_libraries(crashpad_util PUBLIC zlib) else() @@ -357,5 +356,20 @@ endif() if(WIN32) target_link_libraries(crashpad_util PUBLIC "user32" "version" "winhttp") - target_compile_options(crashpad_util PUBLIC "/wd4201") + if(MSVC) + target_compile_options(crashpad_util PUBLIC "/wd4201") + elseif(MINGW) + target_compile_options(crashpad_util PRIVATE + $<$:-municode> + ) + target_compile_definitions(crashpad_util PRIVATE + "__STDC_VERSION__=199901L" + $<$:__MINGW32__> + ) + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_util PRIVATE + $<$:-Wno-multichar> + ) + endif() endif() diff --git a/util/misc/capture_context_win.asm b/util/misc/capture_context_win.asm index b0093ba1fe..f61430d283 100644 --- a/util/misc/capture_context_win.asm +++ b/util/misc/capture_context_win.asm @@ -214,7 +214,10 @@ endif ; namespace crashpad { ; void CaptureContext(CONTEXT* context); ; } // namespace crashpad -ifdef _M_IX86 + +ifdef __MINGW32__ +CAPTURECONTEXT_SYMBOL equ _ZN8crashpad14CaptureContextEP8_CONTEXT +elseifdef _M_IX86 CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPAU_CONTEXT@@@Z elseifdef _M_X64 CAPTURECONTEXT_SYMBOL equ ?CaptureContext@crashpad@@YAXPEAU_CONTEXT@@@Z diff --git a/util/stdlib/aligned_allocator.cc b/util/stdlib/aligned_allocator.cc index 797a3ac4f8..1cbf7d2a5f 100644 --- a/util/stdlib/aligned_allocator.cc +++ b/util/stdlib/aligned_allocator.cc @@ -18,7 +18,7 @@ #include "build/build_config.h" -#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) +#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) || defined(__MINGW32__) #include #elif defined(OS_WIN) #include @@ -31,7 +31,7 @@ namespace { // library to do so. This works even if C++ exceptions are disabled, causing // program termination if uncaught. void ThrowBadAlloc() { -#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) +#if defined(OS_POSIX) || defined(_LIBCPP_STD_VER) || defined(__MINGW32__) // This works with both libc++ and libstdc++. std::__throw_bad_alloc(); #elif defined(OS_WIN) diff --git a/util/win/process_info.cc b/util/win/process_info.cc index 49a6f27fd4..5630153964 100644 --- a/util/win/process_info.cc +++ b/util/win/process_info.cc @@ -115,11 +115,11 @@ bool ReadStruct(HANDLE process, WinVMAddress at, T* into) { &bytes_read)) { // We don't have a name for the type we're reading, so include the signature // to get the type of T. - PLOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__; + PLOG(ERROR) << "ReadProcessMemory " << FUNCTION_SIGNATURE; return false; } if (bytes_read != sizeof(T)) { - LOG(ERROR) << "ReadProcessMemory " << __FUNCSIG__ << " incorrect size"; + LOG(ERROR) << "ReadProcessMemory " << FUNCTION_SIGNATURE << " incorrect size"; return false; } return true; From 4ec46c337905a10f1690ef57594daa11d6e08644 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 23 Mar 2020 08:49:51 +0100 Subject: [PATCH 013/146] docs: Document MinGW changes --- README.getsentry.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.getsentry.md b/README.getsentry.md index a0af84edb4..693e96f03b 100644 --- a/README.getsentry.md +++ b/README.getsentry.md @@ -5,6 +5,7 @@ - Add `throws` declaration to `memfd_create` for compatibility with different libc versions. - Build System Changes Listed Below +- MinGW build support. # Build System Changes @@ -47,6 +48,13 @@ included files): 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` + # How To Update - Bump the submodules to the commit hashes specified in `./DEPS` From f71a7bbbffb618a74a4cd0088beb8c4f1dab90fa Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 1 Apr 2020 09:50:43 +0200 Subject: [PATCH 014/146] cmake: add cmake install + export (#7) --- .gitmodules | 3 + CMakeLists.txt | 156 +++++++++++++---------- client/CMakeLists.txt | 56 +++++--- compat/CMakeLists.txt | 34 ++++- crashpad-config.cmake.in | 10 ++ handler/CMakeLists.txt | 39 +++--- minidump/CMakeLists.txt | 23 ++-- snapshot/CMakeLists.txt | 40 +++--- third_party/getopt/CMakeLists.txt | 21 ++- third_party/lss/lss | 1 + third_party/mini_chromium/CMakeLists.txt | 58 ++++++--- third_party/zlib/CMakeLists.txt | 131 +++++++++++-------- tools/CMakeLists.txt | 106 ++++++++++++++- util/CMakeLists.txt | 119 ++++++++--------- 14 files changed, 530 insertions(+), 267 deletions(-) create mode 100644 crashpad-config.cmake.in create mode 160000 third_party/lss/lss diff --git a/.gitmodules b/.gitmodules index 6b27a29859..d183aa487d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [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/CMakeLists.txt b/CMakeLists.txt index 371960d5d1..83bbe79de3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,47 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.10) project(crashpad LANGUAGES C CXX) +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}") + +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() + +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(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINUX TRUE) +endif() + if(WIN32) enable_language(ASM_MASM) IF(MINGW) @@ -9,7 +50,7 @@ if(WIN32) SET(CMAKE_ASM_MASM_COMPILER "uasm") SET(CMAKE_ASM_MASM_FLAGS "-win64 -10") else() - message(FATAL_ERROR "UASM is required for MinGW builds ! Make sure you have it installed") + message(FATAL_ERROR "UASM is required for MinGW builds! Make sure you have it installed") endif() endif() else() @@ -17,78 +58,65 @@ else() endif() set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(LINUX TRUE) -endif() +set(CMAKE_CXX_STANDARD_REQUIRED ON) -include_directories("${CMAKE_CURRENT_SOURCE_DIR}") -include_directories("${CMAKE_CURRENT_SOURCE_DIR}/third_party/mini_chromium/mini_chromium") - -# These should really be in `compat`, but we want them for the whole project -if(APPLE) - include_directories(SYSTEM "compat/mac") -else() - include_directories(PUBLIC "compat/non_mac") -endif() - -if(LINUX OR ANDROID) - include_directories(SYSTEM "compat/linux") -endif() -if(ANDROID) - include_directories(SYSTEM "compat/android") -endif() - -if(MSVC) - include_directories(SYSTEM "compat/win") -elseif(MINGW) - include_directories(PUBLIC "compat/mingw") -else() - include_directories(PUBLIC "compat/non_win") -endif() - -if(NOT LINUX AND NOT ANDROID) - include_directories(SYSTEM "compat/non_elf") -endif() +add_library(crashpad_interface INTERFACE) +target_include_directories(crashpad_interface INTERFACE + $ + $ +) +target_compile_definitions(crashpad_interface INTERFACE + CRASHPAD_LSS_SOURCE_EMBEDDED +) if(MSVC) - add_definitions( - /DNOMINMAX - /DUNICODE - /DWIN32_LEAN_AND_MEAN - /D_CRT_SECURE_NO_WARNINGS - /D_HAS_EXCEPTIONS=0 - /D_UNICODE - /FS - /W4 - /WX - /Zi - /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. + target_compile_definitions(crashpad_interface INTERFACE + NOMINMAX + UNICODE + WIN32_LEAN_AND_MEAN + _CRT_SECURE_NO_WARNINGS + _HAS_EXCEPTIONS=0 + _UNICODE ) -elseif(MINGW) - #redirect to wmain - add_link_options( - -municode + target_compile_options(crashpad_interface INTERFACE + $<$:/FS> + $<$:/W4> + $<$:/WX> + $<$:/Zi> + $<$:/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(client) add_subdirectory(compat) -add_subdirectory(handler) add_subdirectory(minidump) add_subdirectory(snapshot) -add_subdirectory(tools) add_subdirectory(util) add_subdirectory(third_party/mini_chromium) +add_subdirectory(client) -if(MSVC) - add_subdirectory(third_party/getopt) - add_subdirectory(third_party/zlib) -endif() +add_subdirectory(third_party/zlib) +add_subdirectory(third_party/getopt) + +add_subdirectory(tools) +add_subdirectory(handler) + +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() \ No newline at end of file diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 77787c13be..57d10a9eb8 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,4 +1,4 @@ -list(APPEND CLIENT_SOURCES +add_library(crashpad_client STATIC annotation.cc annotation.h annotation_list.cc @@ -18,7 +18,7 @@ list(APPEND CLIENT_SOURCES ) if(APPLE) - list(APPEND CLIENT_SOURCES + target_sources(crashpad_client PRIVATE crash_report_database_mac.mm crashpad_client_mac.cc simulate_crash_mac.cc @@ -27,37 +27,47 @@ if(APPLE) endif() if(LINUX OR ANDROID) - list(APPEND CLIENT_SOURCES - crashpad_client_linux.cc - simulate_crash_linux.h - client_argv_handling.cc - client_argv_handling.h - crashpad_info_note.S - crash_report_database_generic.cc + target_sources(crashpad_client PRIVATE + crashpad_client_linux.cc + simulate_crash_linux.h + client_argv_handling.cc + client_argv_handling.h + crashpad_info_note.S + crash_report_database_generic.cc ) endif() if(WIN32) - list(APPEND CLIENT_SOURCES - crash_report_database_win.cc - crashpad_client_win.cc - simulate_crash_win.h + target_sources(crashpad_client PRIVATE + crash_report_database_win.cc + crashpad_client_win.cc + simulate_crash_win.h ) endif() -add_library(crashpad_client STATIC ${CLIENT_SOURCES}) +target_include_directories(crashpad_client INTERFACE + "$" + "$" +) target_link_libraries(crashpad_client - crashpad_compat - crashpad_util - mini_chromium + 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 "rpcrt4") + target_link_libraries(crashpad_client PRIVATE rpcrt4) if(MSVC) - target_compile_options(crashpad_client PUBLIC "/wd4201") + target_compile_options(crashpad_client PRIVATE "/wd4201") elseif(MINGW) - target_compile_options(crashpad_client PRIVATE + target_compile_options(crashpad_client PUBLIC "-municode" ) endif() @@ -73,3 +83,9 @@ if(WIN32) ) endif() 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/compat/CMakeLists.txt b/compat/CMakeLists.txt index ccf3d562cc..860a6e0731 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -1,4 +1,4 @@ -list(APPEND COMPAT_SOURCES "") +set(COMPAT_SOURCES) if(APPLE) list(APPEND COMPAT_SOURCES @@ -83,13 +83,39 @@ endif() if(APPLE) add_library(crashpad_compat INTERFACE) - list(TRANSFORM COMPAT_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") - target_sources(crashpad_compat INTERFACE ${COMPAT_SOURCES}) + 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(MSVC) - target_link_libraries(crashpad_compat getopt) + 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} "$") +else() + target_include_directories(crashpad_compat ${TI_TYPE} "$") endif() + +if(LINUX OR ANDROID) + target_include_directories(crashpad_compat ${TI_YPE} "$") +endif() + +if(ANDROID) + target_include_directories(crashpad_compat ${TI_YPE} "$") +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/crashpad-config.cmake.in b/crashpad-config.cmake.in new file mode 100644 index 0000000000..846797d774 --- /dev/null +++ b/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/handler/CMakeLists.txt b/handler/CMakeLists.txt index 8b76bd39d2..3d9dbe4dc1 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -1,4 +1,4 @@ -list(APPEND HANDLER_SOURCES +add_executable(crashpad_handler WIN32 main.cc crash_report_upload_thread.cc crash_report_upload_thread.h @@ -13,7 +13,7 @@ list(APPEND HANDLER_SOURCES ) if(APPLE) - list(APPEND HANDLER_SOURCES + target_sources(crashpad_handler PRIVATE mac/crash_report_exception_handler.cc mac/crash_report_exception_handler.h mac/exception_handler_server.cc @@ -24,7 +24,7 @@ if(APPLE) endif() if(LINUX OR ANDROID) - list(APPEND HANDLER_SOURCES + target_sources(crashpad_handler PRIVATE linux/capture_snapshot.cc linux/capture_snapshot.h linux/crash_report_exception_handler.cc @@ -35,36 +35,35 @@ if(LINUX OR ANDROID) endif() if(LINUX) - list(APPEND HANDLER_SOURCES + target_sources(crashpad_handler PRIVATE linux/cros_crash_report_exception_handler.cc linux/cros_crash_report_exception_handler.h ) endif() if(WIN32) - list(APPEND HANDLER_SOURCES + target_sources(crashpad_handler PRIVATE win/crash_report_exception_handler.cc win/crash_report_exception_handler.h ) endif() -if(WIN32) - add_executable(crashpad_handler WIN32 ${HANDLER_SOURCES}) -else() - add_executable(crashpad_handler ${HANDLER_SOURCES}) -endif() target_link_libraries(crashpad_handler - crashpad_client - crashpad_minidump - crashpad_snapshot - crashpad_tools - crashpad_util - mini_chromium + PRIVATE + $ + PUBLIC + crashpad_client + crashpad_getopt + crashpad_minidump + crashpad_snapshot + crashpad_tools + crashpad_util + mini_chromium ) if(WIN32) if(MSVC) - target_compile_options(crashpad_handler PUBLIC "/wd4201") + target_compile_options(crashpad_handler PRIVATE "/wd4201") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(crashpad_handler PRIVATE @@ -73,3 +72,9 @@ if(WIN32) endif() endif() +set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME handler) +add_executable(crashpad::handler ALIAS crashpad_handler) + +install(TARGETS crashpad_handler EXPORT crashpad_export + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" +) diff --git a/minidump/CMakeLists.txt b/minidump/CMakeLists.txt index 0a2e0dda13..83ed22f121 100644 --- a/minidump/CMakeLists.txt +++ b/minidump/CMakeLists.txt @@ -1,4 +1,4 @@ -list(APPEND MINIDUMP_SOURCES +add_library(crashpad_minidump STATIC minidump_annotation_writer.cc minidump_annotation_writer.h minidump_byte_array_writer.cc @@ -52,20 +52,27 @@ list(APPEND MINIDUMP_SOURCES minidump_writer_util.h ) -add_library(crashpad_minidump STATIC ${MINIDUMP_SOURCES}) target_link_libraries(crashpad_minidump - crashpad_compat - crashpad_snapshot - crashpad_util - mini_chromium + PRIVATE + $ + PUBLIC + crashpad_compat + crashpad_snapshot + crashpad_util + mini_chromium ) if(MSVC) - target_compile_options(crashpad_minidump PUBLIC "/wd4201" "/wd4324") + target_compile_options(crashpad_minidump PRIVATE "/wd4201" "/wd4324") endif() if(WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(crashpad_minidump PRIVATE "-Wno-multichar" ) -endif() \ No newline at end of file +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/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index 698997c1cd..4bbd757676 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -1,4 +1,4 @@ -list(APPEND SNAPSHOT_SOURCES +add_library(crashpad_snapshot STATIC annotation_snapshot.cc annotation_snapshot.h capture_memory.cc @@ -48,7 +48,7 @@ list(APPEND SNAPSHOT_SOURCES if(APPLE) - list(APPEND SNAPSHOT_SOURCES + target_sources(crashpad_snapshot PRIVATE posix/timezone.cc posix/timezone.h mac/cpu_context_mac.cc @@ -81,14 +81,14 @@ if(APPLE) mac/thread_snapshot_mac.h ) else() - list(APPEND SNAPSHOT_SOURCES + target_sources(crashpad_snapshot PRIVATE crashpad_types/crashpad_info_reader.cc crashpad_types/crashpad_info_reader.h ) endif() if(LINUX OR ANDROID) - list(APPEND SNAPSHOT_SOURCES + target_sources(crashpad_snapshot PRIVATE posix/timezone.cc posix/timezone.h linux/cpu_context_linux.cc @@ -130,7 +130,7 @@ if(LINUX OR ANDROID) endif() if(WIN32) - list(APPEND SNAPSHOT_SOURCES + target_sources(crashpad_snapshot PRIVATE win/capture_memory_delegate_win.cc win/capture_memory_delegate_win.h win/cpu_context_win.cc @@ -160,25 +160,30 @@ if(WIN32) ) endif() -# TODO: if(x86) - list(APPEND SNAPSHOT_SOURCES +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() +endif() -add_library(crashpad_snapshot STATIC ${SNAPSHOT_SOURCES}) +target_include_directories(crashpad_snapshot INTERFACE + "$" +) target_link_libraries(crashpad_snapshot - crashpad_client - crashpad_compat - crashpad_util - mini_chromium + PRIVATE + $ + PUBLIC + crashpad_client + crashpad_compat + crashpad_util + mini_chromium ) if(WIN32) - target_link_libraries(crashpad_snapshot "powrprof") + target_link_libraries(crashpad_snapshot PRIVATE powrprof) if(MSVC) - target_compile_options(crashpad_snapshot PUBLIC "/wd4201") + target_compile_options(crashpad_snapshot PRIVATE "/wd4201") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(crashpad_snapshot PRIVATE @@ -191,3 +196,8 @@ if(WIN32) ) endif() 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/third_party/getopt/CMakeLists.txt b/third_party/getopt/CMakeLists.txt index 7671ca0416..91dd2ff56e 100644 --- a/third_party/getopt/CMakeLists.txt +++ b/third_party/getopt/CMakeLists.txt @@ -1,6 +1,17 @@ -list(APPEND GETOPT_SOURCES - getopt.cc - getopt.h -) +if(MSVC) + add_library(crashpad_getopt STATIC + getopt.cc + getopt.h + ) + target_include_directories(crashpad_getopt PUBLIC + $ + ) + target_link_libraries(crashpad_getopt PRIVATE + $ + ) +else() + add_library(crashpad_getopt INTERFACE) +endif() -add_library(getopt STATIC ${GETOPT_SOURCES}) +set_property(TARGET crashpad_getopt PROPERTY EXPORT_NAME getopt) +add_library(crashpad::getopt ALIAS crashpad_getopt) diff --git a/third_party/lss/lss b/third_party/lss/lss new file mode 160000 index 0000000000..fd00dbbd0c --- /dev/null +++ b/third_party/lss/lss @@ -0,0 +1 @@ +Subproject commit fd00dbbd0c06a309c657d89e9430143b179ff6db diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index c3a3245519..341107a055 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -1,4 +1,10 @@ -list(APPEND MINI_CHROMIUM_SOURCES +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() + +mc_append_sources( ../build/build_config.h atomicops.h atomicops_internals_atomicword_compat.h @@ -66,17 +72,17 @@ list(APPEND MINI_CHROMIUM_SOURCES ) if(NOT MINGW) - list(APPEND MINI_CHROMIUM_SOURCES + mc_append_sources( strings/utf_string_conversion_utils.cc ) else() - list(APPEND MINI_CHROMIUM_SOURCES - ../../utf_string_conversion_utils.mingw.cc + mc_append_sources( + ${CMAKE_CURRENT_SOURCE_DIR}/../../utf_string_conversion_utils.mingw.cc ) endif() if(APPLE) - list(APPEND MINI_CHROMIUM_SOURCES + mc_append_sources( mac/close_nocancel.cc mac/foundation_util.h mac/foundation_util.mm @@ -98,7 +104,7 @@ if(APPLE) endif() if(WIN32) - list(APPEND MINI_CHROMIUM_SOURCES + mc_append_sources( process/process_metrics_win.cc strings/string_util_win.cc strings/string_util_win.h @@ -106,7 +112,7 @@ if(WIN32) threading/thread_local_storage_win.cc ) else() - list(APPEND MINI_CHROMIUM_SOURCES + mc_append_sources( files/file_util_posix.cc posix/eintr_wrapper.h posix/safe_strerror.cc @@ -119,23 +125,29 @@ else() ) endif() -list(TRANSFORM MINI_CHROMIUM_SOURCES PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/mini_chromium/base/") - -add_library(mini_chromium STATIC ${MINI_CHROMIUM_SOURCES}) - if(APPLE) - target_link_libraries(mini_chromium PUBLIC "-framework ApplicationServices") - target_link_libraries(mini_chromium PUBLIC "-framework CoreFoundation") - target_link_libraries(mini_chromium PUBLIC "-framework Foundation") - target_link_libraries(mini_chromium PUBLIC "-framework IOKit") - target_link_libraries(mini_chromium PUBLIC "-framework Security") + target_link_libraries(mini_chromium PUBLIC + "-framework ApplicationServices" + "-framework CoreFoundation" + "-framework Foundation" + "-framework IOKit" + "-framework Security" + ) endif() +target_include_directories(mini_chromium PUBLIC + "$" + $ +) +target_link_libraries(mini_chromium + PRIVATE + $ +) if(WIN32) - target_link_libraries(mini_chromium "advapi32" "kernel32") + target_link_libraries(mini_chromium PRIVATE advapi32 kernel32) if(MSVC) - target_compile_options(mini_chromium PUBLIC "/wd4201" "/wd4996") - target_compile_definitions(mini_chromium PUBLIC + target_compile_options(mini_chromium PRIVATE "/wd4201" "/wd4996") + target_compile_definitions(mini_chromium PRIVATE NOMINMAX UNICODE WIN32_LEAN_AND_MEAN @@ -151,3 +163,11 @@ if(WIN32) ) 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" +) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 562452c9d9..7ed2f58680 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -1,54 +1,83 @@ -list(APPEND ZLIB_SOURCES - zlib/adler32.c - zlib/compress.c - zlib/crc32.c - zlib/crc32.h - zlib/deflate.c - zlib/deflate.h - zlib/gzclose.c - zlib/gzguts.h - zlib/gzlib.c - zlib/gzread.c - zlib/gzwrite.c - zlib/infback.c - zlib/inffast.c - zlib/inffast.h - zlib/inffixed.h - zlib/inflate.c - zlib/inflate.h - zlib/inftrees.c - zlib/inftrees.h - zlib/names.h - zlib/trees.c - zlib/trees.h - zlib/uncompr.c - zlib/zconf.h - zlib/zlib.h - zlib/zutil.c - zlib/zutil.h - zlib_crashpad.h -) - -# TODO: x86/x64 -list(APPEND ZLIB_SOURCES - zlib/crc_folding.c - zlib/fill_window_sse.c - zlib/x86.c - zlib/x86.h -) +if(CRASHPAD_ZLIB_SYSTEM) + add_library(crashpad_zlib INTERFACE) + string(REPLACE ";" "$" GENEX_ZLIB_LIBRARIES "${ZLIB_LIBRARIES}") + target_include_directories(crashpad_zlib INTERFACE + $ + ) + target_compile_definitions(crashpad_zlib INTERFACE + ZLIB_CONST + CRASHPAD_ZLIB_SOURCE_SYSTEM + $ + ) + target_link_libraries(crashpad_zlib INTERFACE $) +else() + add_library(crashpad_zlib STATIC + zlib/adler32.c + zlib/compress.c + zlib/crc32.c + zlib/crc32.h + zlib/deflate.c + zlib/deflate.h + zlib/gzclose.c + zlib/gzguts.h + zlib/gzlib.c + zlib/gzread.c + zlib/gzwrite.c + zlib/infback.c + zlib/inffast.c + zlib/inffast.h + zlib/inffixed.h + zlib/inflate.c + zlib/inflate.h + zlib/inftrees.c + zlib/inftrees.h + zlib/names.h + zlib/trees.c + zlib/trees.h + zlib/uncompr.c + zlib/zconf.h + zlib/zlib.h + zlib/zutil.c + zlib/zutil.h + zlib_crashpad.h + ) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(x86)|(i[3-7]86)|(AMD64)") + target_sources(crashpad_zlib PRIVATE + zlib/crc_folding.c + zlib/fill_window_sse.c + zlib/x86.c + zlib/x86.h + ) + endif() + target_compile_definitions(crashpad_zlib PUBLIC + CRASHPAD_ZLIB_SOURCE_EMBEDDED + ) + target_compile_definitions(crashpad_zlib + PUBLIC + ZLIB_CONST + PRIVATE + HAVE_STDARG_H + ) + target_include_directories(crashpad_zlib PRIVATE + "$" + ) + target_link_libraries(crashpad_zlib PRIVATE + $ + ) -add_library(zlib STATIC ${ZLIB_SOURCES}) -target_compile_definitions(zlib PUBLIC HAVE_STDARG_H) + if(MSVC) + target_compile_options(crashpad_zlib PRIVATE + "/wd4131" # uses old-style declarator + "/wd4244" # conversion from 't1' to 't2', possible loss of data + "/wd4245" # conversion from 't1' to 't2', signed/unsigned mismatch + "/wd4267" # conversion from 'size_t' to 't', possible loss of data + "/wd4324" # structure was padded due to alignment specifier + "/wd4702" # unreachable code + ) + endif() +endif() -include_directories(zlib) +set_property(TARGET crashpad_zlib PROPERTY EXPORT_NAME zlib) +add_library(crashpad::zlib ALIAS crashpad_zlib) -if(MSVC) - target_compile_options(zlib PUBLIC - "/wd4131" # uses old-style declarator - "/wd4244" # conversion from 't1' to 't2', possible loss of data - "/wd4245" # conversion from 't1' to 't2', signed/unsigned mismatch - "/wd4267" # conversion from 'size_t' to 't', possible loss of data - "/wd4324" # structure was padded due to alignment specifier - "/wd4702" # unreachable code - ) -endif() +crashpad_install_target(crashpad_zlib) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index f72f7e6e81..5ad79d5d5e 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,11 +1,105 @@ -list(APPEND TOOLS_SOURCES +add_library(crashpad_tools STATIC tool_support.cc tool_support.h ) - -add_library(crashpad_tools STATIC ${TOOLS_SOURCES}) -target_link_libraries(crashpad_tools - mini_chromium +target_link_libraries(crashpad_tools PRIVATE + $ ) -# TODO: do we need any tool executables? +set_property(TARGET crashpad_tools PROPERTY EXPORT_NAME tools) +add_library(crashpad::tools ALIAS crashpad_tools) + +crashpad_install_target(crashpad_tools) + +if(CRASHPAD_BUILD_TOOLS) + add_executable(crashpad_database_util + crashpad_database_util.cc + ) + target_link_libraries(crashpad_database_util PRIVATE + crashpad_client + crashpad_compat + crashpad_getopt + crashpad_tools + ) + crashpad_install_target(crashpad_database_util) + + add_executable(crashpad_http_upload + crashpad_http_upload.cc + ) + target_link_libraries(crashpad_http_upload PRIVATE + crashpad_client + crashpad_compat + crashpad_getopt + crashpad_tools + crashpad_zlib + mini_chromium + ) + crashpad_install_target(crashpad_http_upload) + + add_executable(crashpad_generate_dump + generate_dump.cc + ) + target_link_libraries(crashpad_generate_dump PRIVATE + crashpad_getopt + crashpad_minidump + crashpad_snapshot + crashpad_tools + mini_chromium + ) + if(APPLE) + # FIXME: cmake 3.13 added target_link_options + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sectcreate __TEXT __info_plist \"${CMAKE_CURRENT_SOURCE_DIR}/mac/sectaskaccess_info.plist\"") + endif() + crashpad_install_target(crashpad_generate_dump) + + if(APPLE) + add_executable(run_with_crashpad + run_with_crashpad.cc + ) + target_link_libraries(run_with_crashpad PRIVATE + crashpad_client + crashpad_compat + crashpad_tools + crashpad_util + mini_chromium + ) + crashpad_install_target(run_with_crashpad) + + add_executable(catch_exception_tool + mac/catch_exception_tool.cc + ) + target_link_libraries(catch_exception_tool PRIVATE + crashpad_compat + crashpad_tools + crashpad_util + mini_chromium + ) + crashpad_install_target(catch_exception_tool) + + add_executable(exception_port_tool + mac/exception_port_tool.cc + ) + target_link_libraries(exception_port_tool PRIVATE + crashpad_compat + crashpad_tools + crashpad_util + mini_chromium + ) + # FIXME: cmake 3.13 added target_link_options + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sectcreate __TEXT __info_plist \"${CMAKE_CURRENT_SOURCE_DIR}/mac/sectaskaccess_info.plist\"") + crashpad_install_target(exception_port_tool) + + add_executable(on_demand_service_tool + mac/on_demand_service_tool.mm + ) + target_link_libraries(on_demand_service_tool PRIVATE + -framework CoreFoundation + -framework Foundation + crashpad_compat + crashpad_tools + crashpad_util + mini_chromium + ) + crashpad_install_target(on_demand_service_tool) + endif() +endif() diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 3a796b8a67..523bfe792d 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -1,4 +1,4 @@ -list(APPEND UTIL_SOURCES +add_library(crashpad_util STATIC file/delimited_file_reader.cc file/delimited_file_reader.h file/directory_reader.h @@ -102,7 +102,7 @@ list(APPEND UTIL_SOURCES ) if(NOT WIN32) - list(APPEND UTIL_SOURCES + target_sources(crashpad_util PRIVATE file/directory_reader_posix.cc file/file_io_posix.cc file/filesystem_posix.cc @@ -130,7 +130,7 @@ if(NOT WIN32) endif() if(APPLE) - list(APPEND UTIL_SOURCES + target_sources(crashpad_util PRIVATE mac/checked_mach_address_range.h mac/launchd.h mac/launchd.mm @@ -183,7 +183,7 @@ if(APPLE) endif() if(LINUX OR ANDROID) - list(APPEND UTIL_SOURCES + target_sources(crashpad_util PRIVATE net/http_transport_socket.cc linux/address_types.h linux/auxiliary_vector.cc @@ -231,7 +231,7 @@ if(LINUX OR ANDROID) endif() if(WIN32) - list(APPEND UTIL_SOURCES + target_sources(crashpad_util PRIVATE file/directory_reader_win.cc file/file_io_win.cc file/filesystem_win.cc @@ -291,73 +291,71 @@ endif() # Copied from: https://github.com/qedsoftware/crashpad/blob/3583c50a6575857abcf140f6ea3b8d11390205b3/util/CMakeLists.txt#L196-L233 if(APPLE) - set(def_relative_files "exc.defs" "mach_exc.defs" "notify.defs") - set(input_files "") - foreach(x ${def_relative_files}) - # CMAKE_OSX_SYSROOT may be empty (e.g. for Makefile generators), - # in this case files will be taken from root. - set(full_path "${CMAKE_OSX_SYSROOT}/usr/include/mach/${x}") - if(NOT EXISTS "${full_path}") - message(FATAL_ERROR "File not found: ${full_path}") - endif() - list(APPEND input_files "${full_path}") - endforeach() - list(APPEND input_files "${CMAKE_CURRENT_LIST_DIR}/mach/child_port.defs") + set(def_relative_files "exc.defs" "mach_exc.defs" "notify.defs") + set(input_files "") + foreach(x ${def_relative_files}) + # CMAKE_OSX_SYSROOT may be empty (e.g. for Makefile generators), + # in this case files will be taken from root. + set(full_path "${CMAKE_OSX_SYSROOT}/usr/include/mach/${x}") + if(NOT EXISTS "${full_path}") + message(FATAL_ERROR "File not found: ${full_path}") + endif() + list(APPEND input_files "${full_path}") + endforeach() + list(APPEND input_files "${CMAKE_CURRENT_LIST_DIR}/mach/child_port.defs") - find_package(PythonInterp 2.7 REQUIRED) + find_package(PythonInterp 2.7 REQUIRED) - set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/util/mach") - file(MAKE_DIRECTORY "${output_dir}") + set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/util/mach") + file(MAKE_DIRECTORY "${output_dir}") - # Create generate rule for each input file. Add each generated output - # as a source to the target. - foreach(input ${input_files}) - get_filename_component(name_we "${input}" NAME_WE) - set(output_files "") - foreach(suffix "User.c" "Server.c" ".h" "Server.h") - list(APPEND output_files "${output_dir}/${name_we}${suffix}") + # Create generate rule for each input file. Add each generated output + # as a source to the target. + foreach(input ${input_files}) + get_filename_component(name_we "${input}" NAME_WE) + set(output_files "") + foreach(suffix "User.c" "Server.c" ".h" "Server.h") + list(APPEND output_files "${output_dir}/${name_we}${suffix}") + endforeach() + add_custom_command( + OUTPUT + ${output_files} + COMMAND + "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" ${output_files} + DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" + ) + target_sources(crashpad_util PRIVATE ${output_files}) endforeach() - add_custom_command( - OUTPUT - ${output_files} - COMMAND - "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" ${output_files} - DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" - ) - list(APPEND UTIL_SOURCES ${output_files}) - endforeach() - include_directories("${CMAKE_CURRENT_BINARY_DIR}") + include_directories("${CMAKE_CURRENT_BINARY_DIR}") endif() -add_library(crashpad_util STATIC ${UTIL_SOURCES}) -target_link_libraries(crashpad_util PUBLIC - crashpad_compat - mini_chromium +target_include_directories(crashpad_util PRIVATE + $ +) +target_link_libraries(crashpad_util + PRIVATE + $ + PUBLIC + crashpad_compat + crashpad_zlib + mini_chromium ) - -target_compile_definitions(crashpad_util PUBLIC "-DZLIB_CONST") - -if(MSVC) - target_compile_definitions(crashpad_util PUBLIC "-DCRASHPAD_ZLIB_SOURCE_EMBEDDED") - target_link_libraries(crashpad_util PUBLIC zlib) -else() - target_compile_definitions(crashpad_util PUBLIC "-DCRASHPAD_ZLIB_SOURCE_SYSTEM") - target_link_libraries(crashpad_util PUBLIC "z") -endif() if(APPLE) - target_link_libraries(crashpad_util PUBLIC "bsm") - target_link_libraries(crashpad_util PUBLIC "-framework CoreFoundation") - target_link_libraries(crashpad_util PUBLIC "-framework Foundation") - target_link_libraries(crashpad_util PUBLIC "-framework IOKit") + target_link_libraries(crashpad_util PRIVATE + bsm + "-framework CoreFoundation" + "-framework Foundation" + "-framework IOKit" + ) endif() if(WIN32) - target_link_libraries(crashpad_util PUBLIC "user32" "version" "winhttp") + target_link_libraries(crashpad_util PRIVATE user32 version winhttp) if(MSVC) - target_compile_options(crashpad_util PUBLIC "/wd4201") + target_compile_options(crashpad_util PRIVATE "/wd4201") elseif(MINGW) target_compile_options(crashpad_util PRIVATE $<$:-municode> @@ -373,3 +371,8 @@ if(WIN32) ) endif() endif() + +set_property(TARGET crashpad_util PROPERTY EXPORT_NAME util) +add_library(crashpad::util ALIAS crashpad_util) + +crashpad_install_target(crashpad_util) From 23474e346aa7c24a27506ee038b22bae74a5ce75 Mon Sep 17 00:00:00 2001 From: Javier Blazquez Date: Wed, 1 Apr 2020 00:53:05 -0700 Subject: [PATCH 015/146] win: Make crashpad_handler a GUI app (#6) This change adds a missing `/SUBSYSTEM:WINDOWS` option to the linker command line on Windows for the `crashpad_handler.exe` binary. Without this option (which the regular Crashpad build system [also adds](https://github.com/getsentry/crashpad/blob/getsentry/handler/handler.gyp#L90)) the resulting executable will open an empty console window on startup, which is particularly unsightly for Windows GUI applications integrating `sentry-native` which would otherwise have no console windows. --- handler/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 3d9dbe4dc1..f9d23b6949 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -64,6 +64,7 @@ target_link_libraries(crashpad_handler if(WIN32) if(MSVC) target_compile_options(crashpad_handler PRIVATE "/wd4201") + target_link_options(crashpad_handler PRIVATE "/SUBSYSTEM:WINDOWS") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(crashpad_handler PRIVATE From 3da8bcf64b01e32a84385b042e707b2561827a68 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Wed, 8 Apr 2020 22:06:51 +0200 Subject: [PATCH 016/146] build(cmake): remove /W3 from CMAKE_C_FLAGS and CMAKE_CXX_FLAGS (#8) --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 83bbe79de3..56a885b1dd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,8 @@ if(MSVC) _HAS_EXCEPTIONS=0 _UNICODE ) + string(REGEX REPLACE "/[Ww][0123]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REGEX REPLACE "/[Ww][0123]" "" CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") target_compile_options(crashpad_interface INTERFACE $<$:/FS> $<$:/W4> @@ -119,4 +121,4 @@ if(CRASHPAD_ENABLE_INSTALL_DEV) INSTALL_DESTINATION "${CMAKE_INSTALL_CMAKEDIR}" ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/crashpad-config.cmake" DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") -endif() \ No newline at end of file +endif() From f8c8a966ea9fc8f5cfc140f8c9520d1692b85488 Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Thu, 9 Apr 2020 18:55:02 +0200 Subject: [PATCH 017/146] build(cmake): use CMAKE_CXX_FLAGS as source of regex for CMAKE_CXX_FLAGS (#9) --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 56a885b1dd..6b4d207e49 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,7 +79,7 @@ if(MSVC) _UNICODE ) string(REGEX REPLACE "/[Ww][0123]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") - string(REGEX REPLACE "/[Ww][0123]" "" CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}") + string(REGEX REPLACE "/[Ww][0123]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") target_compile_options(crashpad_interface INTERFACE $<$:/FS> $<$:/W4> From 2f362fea4cff4bd51e14125db55dc1dca15b8631 Mon Sep 17 00:00:00 2001 From: Amphaal Date: Fri, 10 Apr 2020 11:17:27 +0200 Subject: [PATCH 018/146] build: Enhance MinGW compatibility for cross compilation (#10) --- CMakeLists.txt | 14 +++++++------- third_party/mini_chromium/CMakeLists.txt | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6b4d207e49..dfe31311a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,13 +44,13 @@ endif() if(WIN32) enable_language(ASM_MASM) - IF(MINGW) - find_program(UASM uasm) - if(UASM) - SET(CMAKE_ASM_MASM_COMPILER "uasm") - SET(CMAKE_ASM_MASM_FLAGS "-win64 -10") - else() - message(FATAL_ERROR "UASM is required for MinGW builds! Make sure you have it installed") + 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() diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 341107a055..8cdff52175 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -77,7 +77,7 @@ if(NOT MINGW) ) else() mc_append_sources( - ${CMAKE_CURRENT_SOURCE_DIR}/../../utf_string_conversion_utils.mingw.cc + ../../utf_string_conversion_utils.mingw.cc ) endif() From 944c775539da90dd5dd65401b6aabb0a0159f94a Mon Sep 17 00:00:00 2001 From: Amphaal Date: Tue, 14 Apr 2020 08:41:40 +0200 Subject: [PATCH 019/146] build: MinGW dbghelp.h C-compatible (#11) --- compat/mingw/dbghelp.h | 127 ++++++++++++++++++++++++++++++++++------- 1 file changed, 106 insertions(+), 21 deletions(-) diff --git a/compat/mingw/dbghelp.h b/compat/mingw/dbghelp.h index 45efe1920c..b492198485 100644 --- a/compat/mingw/dbghelp.h +++ b/compat/mingw/dbghelp.h @@ -15,32 +15,17 @@ #ifndef CRASHPAD_COMPAT_MINGW_DBGHELP_H_ #define CRASHPAD_COMPAT_MINGW_DBGHELP_H_ -#include_next +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-include-next" +#include_next #include - -#include "base/strings/string16.h" #include -#include #include +#include //! \file -//! \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 XSAVE-managed state stored within CPU-specific //! context structures. struct __attribute__((packed, aligned(4))) XSTATE_CONFIG_FEATURE_MSC_INFO { @@ -126,6 +111,23 @@ struct __attribute__((packed, aligned(4))) XSTATE_CONFIG_FEATURE_MSC_INFO { //! \} +#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. //! @@ -208,7 +210,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4 //! //! On Windows 8.1 (NT 6.3), this is “6.3.9600.17031 //! (winblue_gdr.140221-1952)”. - base::char16 BuildString[260]; + wchar_t BuildString[260]; //! \brief The minidump producer’s “build string”, a string identifying the //! module that produced a minidump file. @@ -217,7 +219,7 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4 //! //! 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. - base::char16 DbgBldStr[40]; + wchar_t DbgBldStr[40]; }; //! \brief Information about the process that the minidump file contains a @@ -246,4 +248,87 @@ struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_5 //! \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_ From 66c69eece9b43c78fe3d0957f708f2f2a461b1f3 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 14 Apr 2020 10:49:05 +0200 Subject: [PATCH 020/146] build: bump submodules to versions specified in DEPS --- third_party/lss/lss | 2 +- third_party/mini_chromium/mini_chromium | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/third_party/lss/lss b/third_party/lss/lss index fd00dbbd0c..7bde79cc27 160000 --- a/third_party/lss/lss +++ b/third_party/lss/lss @@ -1 +1 @@ -Subproject commit fd00dbbd0c06a309c657d89e9430143b179ff6db +Subproject commit 7bde79cc274d06451bf65ae82c012a5d3e476b5a diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index f8f1182adb..bbf1307928 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit f8f1182adb804675b2aa4fb3ce03f6f884fae474 +Subproject commit bbf1307928bb7a9d1eda6be576283c8093b2775b From 1d9a0b7dd3eeaefb927fd3c827ca9b060f4018f0 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 14 Apr 2020 11:17:53 +0200 Subject: [PATCH 021/146] build: sync sources --- README.getsentry.md | 2 +- third_party/mini_chromium/CMakeLists.txt | 3 ++- util/CMakeLists.txt | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.getsentry.md b/README.getsentry.md index 693e96f03b..0c35f9f987 100644 --- a/README.getsentry.md +++ b/README.getsentry.md @@ -41,7 +41,7 @@ included files): - `./snapshot/CMakeLists.txt` - `./third_party/getopt/CMakeLists.txt` - `./third_party/mini_chromium/CMakeLists.txt` -- `./third_party/lib/CMakeLists.txt` +- `./third_party/zlib/CMakeLists.txt` - `./tools/CMakeLists.txt` - `./util/CMakeLists.txt` diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 8cdff52175..3774a62c0a 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -44,7 +44,7 @@ mc_append_sources( process/process_metrics.h rand_util.cc rand_util.h - scoped_clear_errno.h + scoped_clear_last_error.h scoped_generic.h stl_util.h strings/string16.cc @@ -106,6 +106,7 @@ endif() if(WIN32) mc_append_sources( process/process_metrics_win.cc + scoped_clear_last_error_win.cc strings/string_util_win.cc strings/string_util_win.h synchronization/lock_impl_win.cc diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 523bfe792d..93b0052c90 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -11,6 +11,8 @@ add_library(crashpad_util STATIC file/file_writer.cc file/file_writer.h file/filesystem.h + file/output_stream_file_writer.cc + file/output_stream_file_writer.h file/scoped_remove_file.cc file/scoped_remove_file.h file/string_file.cc @@ -86,6 +88,14 @@ add_library(crashpad_util STATIC stdlib/strnlen.cc stdlib/strnlen.h stdlib/thread_safe_vector.h + stream/base94_output_stream.cc + stream/base94_output_stream.h + stream/file_encoder.cc + stream/file_encoder.h + stream/file_output_stream.cc + stream/file_output_stream.h + stream/log_output_stream.cc + stream/log_output_stream.h stream/output_stream_interface.h stream/zlib_output_stream.cc stream/zlib_output_stream.h @@ -182,6 +192,13 @@ if(APPLE) ) endif() +if(ANDROID) + target_sources(crashpad_util PRIVATE + linux/initial_signal_dispositions.cc + linux/initial_signal_dispositions.h + ) +endif() + if(LINUX OR ANDROID) target_sources(crashpad_util PRIVATE net/http_transport_socket.cc @@ -260,6 +277,8 @@ if(WIN32) win/handle.h win/initial_client_data.cc win/initial_client_data.h + win/loader_lock.cc + win/loader_lock.h win/module_version.cc win/module_version.h win/nt_internals.cc From 4f3e36260fb7c97b717ad1fdc34ec0771328cc24 Mon Sep 17 00:00:00 2001 From: Amphaal Date: Sat, 18 Apr 2020 10:02:00 +0200 Subject: [PATCH 022/146] build(MinGW) Work around missing include (#13) --- util/CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 93b0052c90..121dac9874 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -383,6 +383,13 @@ if(WIN32) "__STDC_VERSION__=199901L" $<$:__MINGW32__> ) + #exception_handler_server.cc missing header ? + set_source_files_properties( + win/exception_handler_server.cc + PROPERTIES + COMPILE_FLAGS + "-include memory" + ) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(crashpad_util PRIVATE From 55b7a3cc58e16e3bdd036060b266ace1edeef89a Mon Sep 17 00:00:00 2001 From: Javier Blazquez Date: Wed, 17 Jun 2020 13:21:51 -0700 Subject: [PATCH 023/146] cmake: Expose crashpad_handler static library (#14) --- handler/CMakeLists.txt | 51 +++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index f9d23b6949..6f14b6cd05 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -1,5 +1,4 @@ -add_executable(crashpad_handler WIN32 - main.cc +add_library(crashpad_handler_lib STATIC crash_report_upload_thread.cc crash_report_upload_thread.h handler_main.cc @@ -13,7 +12,7 @@ add_executable(crashpad_handler WIN32 ) if(APPLE) - target_sources(crashpad_handler PRIVATE + target_sources(crashpad_handler_lib PRIVATE mac/crash_report_exception_handler.cc mac/crash_report_exception_handler.h mac/exception_handler_server.cc @@ -24,7 +23,7 @@ if(APPLE) endif() if(LINUX OR ANDROID) - target_sources(crashpad_handler PRIVATE + target_sources(crashpad_handler_lib PRIVATE linux/capture_snapshot.cc linux/capture_snapshot.h linux/crash_report_exception_handler.cc @@ -35,25 +34,57 @@ if(LINUX OR ANDROID) endif() if(LINUX) - target_sources(crashpad_handler PRIVATE + 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 PRIVATE + 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_compile_options(crashpad_handler_lib PRIVATE "/wd4201") + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_handler_lib PRIVATE + "-Wno-multichar" + ) + endif() +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) + +add_executable(crashpad_handler WIN32 + main.cc +) + target_link_libraries(crashpad_handler PRIVATE $ PUBLIC crashpad_client crashpad_getopt + crashpad_handler_lib crashpad_minidump crashpad_snapshot crashpad_tools @@ -61,16 +92,10 @@ target_link_libraries(crashpad_handler mini_chromium ) -if(WIN32) +if(WIN32) if(MSVC) - target_compile_options(crashpad_handler PRIVATE "/wd4201") target_link_options(crashpad_handler PRIVATE "/SUBSYSTEM:WINDOWS") endif() - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(crashpad_handler PRIVATE - "-Wno-multichar" - ) - endif() endif() set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME handler) From 49efa56e5ed5ff37d551893358d75885c6ef9aab Mon Sep 17 00:00:00 2001 From: Javier Blazquez Date: Thu, 18 Jun 2020 00:46:46 -0700 Subject: [PATCH 024/146] fix: Make linux build work (#15) --- CMakeLists.txt | 2 +- compat/CMakeLists.txt | 8 ++++++-- third_party/mini_chromium/CMakeLists.txt | 5 +++++ util/CMakeLists.txt | 5 +++++ 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dfe31311a0..8b9ca022c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.12) project(crashpad LANGUAGES C CXX) set(CRASHPAD_MAIN_PROJECT OFF) diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt index 860a6e0731..ca660ed2db 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -93,6 +93,10 @@ else() ) endif() +if(LINUX) + target_link_libraries(crashpad_compat PRIVATE dl) +endif() + if(MSVC) target_include_directories(crashpad_compat ${TI_TYPE} "$") elseif(MINGW) @@ -108,11 +112,11 @@ else() endif() if(LINUX OR ANDROID) - target_include_directories(crashpad_compat ${TI_YPE} "$") + target_include_directories(crashpad_compat ${TI_TYPE} "$") endif() if(ANDROID) - target_include_directories(crashpad_compat ${TI_YPE} "$") + target_include_directories(crashpad_compat ${TI_TYPE} "$") endif() set_property(TARGET crashpad_compat PROPERTY EXPORT_NAME compat) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 3774a62c0a..6e1c126564 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -135,6 +135,11 @@ if(APPLE) "-framework Security" ) endif() + +if(LINUX) + target_link_libraries(mini_chromium PRIVATE pthread) +endif() + target_include_directories(mini_chromium PUBLIC "$" $ diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 121dac9874..48234c2a1b 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -239,6 +239,7 @@ if(LINUX OR ANDROID) linux/traits.h misc/capture_context_linux.S misc/paths_linux.cc + misc/time_linux.cc posix/process_info_linux.cc process/process_memory_linux.cc process/process_memory_linux.h @@ -371,6 +372,10 @@ if(APPLE) ) endif() +if(LINUX) + target_link_libraries(crashpad_util PRIVATE pthread) +endif() + if(WIN32) target_link_libraries(crashpad_util PRIVATE user32 version winhttp) if(MSVC) From 1ca6debce9b972f9dc7f8a4c54458e6d1956ab5f Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 26 Jun 2020 12:42:56 +0200 Subject: [PATCH 025/146] build: Update CMake files and submodules --- README.getsentry.md | 11 +++- client/CMakeLists.txt | 17 +++++- compat/CMakeLists.txt | 17 ++++++ handler/CMakeLists.txt | 52 ++++++++-------- snapshot/CMakeLists.txt | 21 ++++++- third_party/mini_chromium/CMakeLists.txt | 31 +++++++++- third_party/mini_chromium/mini_chromium | 2 +- util/CMakeLists.txt | 78 ++++++++++++++++-------- 8 files changed, 171 insertions(+), 58 deletions(-) diff --git a/README.getsentry.md b/README.getsentry.md index 0c35f9f987..c7824e1ce9 100644 --- a/README.getsentry.md +++ b/README.getsentry.md @@ -4,7 +4,7 @@ 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 +- Build System Changes Listed Below. - MinGW build support. # Build System Changes @@ -55,6 +55,15 @@ 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` diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 57d10a9eb8..b1a77c1944 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -17,7 +17,7 @@ add_library(crashpad_client STATIC simulate_crash.h ) -if(APPLE) +if(APPLE AND NOT IOS) target_sources(crashpad_client PRIVATE crash_report_database_mac.mm crashpad_client_mac.cc @@ -26,6 +26,13 @@ if(APPLE) ) endif() +if(IOS) + target_sources(crashpad_client PRIVATE + crash_report_database_mac.mm + crashpad_client_ios.cc + ) +endif() + if(LINUX OR ANDROID) target_sources(crashpad_client PRIVATE crashpad_client_linux.cc @@ -84,6 +91,14 @@ if(WIN32) endif() endif() +if(IOS) + target_link_libraries(crashpad_client + PUBLIC + crashpad_minidump + crashpad_snapshot + ) +endif() + crashpad_install_target(crashpad_client) crashpad_install_dev(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crashpad/client" diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt index ca660ed2db..33870a5319 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -5,6 +5,7 @@ if(APPLE) mac/AvailabilityMacros.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 ) @@ -17,6 +18,16 @@ else() ) 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 @@ -111,8 +122,14 @@ else() 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} "$") +else() + target_include_directories(crashpad_compat ${TI_TYPE} "$") endif() if(ANDROID) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 6f14b6cd05..a0b5d2deeb 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -74,33 +74,35 @@ add_library(crashpad::handler_lib ALIAS crashpad_handler_lib) crashpad_install_target(crashpad_handler_lib) -add_executable(crashpad_handler WIN32 - main.cc -) +if(NOT IOS) + add_executable(crashpad_handler WIN32 + main.cc + ) -target_link_libraries(crashpad_handler - PRIVATE - $ - PUBLIC - crashpad_client - crashpad_getopt - crashpad_handler_lib - crashpad_minidump - crashpad_snapshot - crashpad_tools - crashpad_util - mini_chromium -) + 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") + if(WIN32) + if(MSVC) + target_link_options(crashpad_handler PRIVATE "/SUBSYSTEM:WINDOWS") + endif() endif() -endif() -set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME handler) -add_executable(crashpad::handler ALIAS crashpad_handler) + set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME handler) + add_executable(crashpad::handler ALIAS crashpad_handler) -install(TARGETS crashpad_handler EXPORT crashpad_export - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" -) + install(TARGETS crashpad_handler EXPORT crashpad_export + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) +endif() \ No newline at end of file diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index 4bbd757676..bc8ec7cf2c 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -47,7 +47,7 @@ add_library(crashpad_snapshot STATIC ) -if(APPLE) +if(APPLE AND NOT IOS) target_sources(crashpad_snapshot PRIVATE posix/timezone.cc posix/timezone.h @@ -80,6 +80,25 @@ if(APPLE) 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.cc + ios/exception_snapshot_ios.h + ios/memory_snapshot_ios.cc + ios/memory_snapshot_ios.h + ios/module_snapshot_ios.cc + ios/module_snapshot_ios.h + ios/process_snapshot_ios.cc + ios/process_snapshot_ios.h + ios/system_snapshot_ios.cc + ios/system_snapshot_ios.h + ios/thread_snapshot_ios.cc + ios/thread_snapshot_ios.h + mac/cpu_context_mac.cc + mac/cpu_context_mac.h + ) else() target_sources(crashpad_snapshot PRIVATE crashpad_types/crashpad_info_reader.cc diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 6e1c126564..320f9aae89 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -11,6 +11,8 @@ mc_append_sources( atomicops_internals_portable.h auto_reset.h bit_cast.h + check.h + check_op.h compiler_specific.h debug/alias.cc debug/alias.h @@ -81,7 +83,7 @@ else() ) endif() -if(APPLE) +if(APPLE AND NOT IOS) mc_append_sources( mac/close_nocancel.cc mac/foundation_util.h @@ -101,6 +103,23 @@ if(APPLE) 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) @@ -126,7 +145,7 @@ else() ) endif() -if(APPLE) +if(APPLE AND NOT IOS) target_link_libraries(mini_chromium PUBLIC "-framework ApplicationServices" "-framework CoreFoundation" @@ -134,6 +153,14 @@ if(APPLE) "-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) diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index bbf1307928..ae14a14ab4 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit bbf1307928bb7a9d1eda6be576283c8093b2775b +Subproject commit ae14a14ab4cea36db9c446741581d427a7fc7f89 diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 48234c2a1b..14f3903131 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -2,6 +2,8 @@ add_library(crashpad_util STATIC file/delimited_file_reader.cc file/delimited_file_reader.h file/directory_reader.h + file/file_helper.cc + file/file_helper.h file/file_io.cc file/file_io.h file/file_reader.cc @@ -141,20 +143,8 @@ endif() if(APPLE) target_sources(crashpad_util PRIVATE - mac/checked_mach_address_range.h - mac/launchd.h - mac/launchd.mm - mac/mac_util.cc - mac/mac_util.h - mac/service_management.cc - mac/service_management.h mac/xattr.cc mac/xattr.h - mach/child_port_handshake.cc - mach/child_port_handshake.h - mach/child_port_server.cc - mach/child_port_server.h - mach/child_port_types.h mach/composite_mach_message_server.cc mach/composite_mach_message_server.h mach/exc_client_variants.cc @@ -165,31 +155,56 @@ if(APPLE) mach/exception_behaviors.h mach/exception_ports.cc mach/exception_ports.h - mach/exception_types.cc - mach/exception_types.h mach/mach_extensions.cc mach/mach_extensions.h mach/mach_message.cc mach/mach_message.h mach/mach_message_server.cc mach/mach_message_server.h - mach/notify_server.cc - mach/notify_server.h - mach/scoped_task_suspend.cc - mach/scoped_task_suspend.h mach/symbolic_constants_mach.cc mach/symbolic_constants_mach.h - mach/task_for_pid.cc - mach/task_for_pid.h misc/capture_context_mac.S misc/clock_mac.cc misc/paths_mac.cc - net/http_transport_mac.mm - posix/process_info_mac.cc - process/process_memory_mac.cc - process/process_memory_mac.h synchronization/semaphore_mac.cc ) + if(NOT IOS) + target_sources(crashpad_util PRIVATE + mac/checked_mach_address_range.h + mac/launchd.h + mac/launchd.mm + mac/mac_util.cc + mac/mac_util.h + mac/service_management.cc + mac/service_management.h + mach/bootstrap.cc + mach/bootstrap.h + mach/child_port_handshake.cc + mach/child_port_handshake.h + mach/child_port_server.cc + mach/child_port_server.h + mach/child_port_types.h + mach/exception_types.cc + mach/exception_types.h + mach/notify_server.cc + mach/notify_server.h + mach/scoped_task_suspend.cc + mach/scoped_task_suspend.h + mach/task_for_pid.cc + mach/task_for_pid.h + net/http_transport_mac.mm + posix/process_info_mac.cc + process/process_memory_mac.cc + process/process_memory_mac.h + ) + elseif() + target_sources(crashpad_util PRIVATE + ios/exception_processor.h + ios/exception_processor.mm + ios/ios_system_data_collector.h + ios/ios_system_data_collector.mm + ) + endif() endif() if(ANDROID) @@ -298,11 +313,13 @@ if(WIN32) win/scoped_local_alloc.h win/scoped_process_suspend.cc win/scoped_process_suspend.h + win/scoped_registry_key.h win/scoped_set_event.cc win/scoped_set_event.h win/session_end_watcher.cc win/session_end_watcher.h win/termination_codes.h + win/traits.h win/xp_compat.h misc/capture_context_win.asm win/safe_terminate_process.asm @@ -311,8 +328,16 @@ endif() # Copied from: https://github.com/qedsoftware/crashpad/blob/3583c50a6575857abcf140f6ea3b8d11390205b3/util/CMakeLists.txt#L196-L233 if(APPLE) - set(def_relative_files "exc.defs" "mach_exc.defs" "notify.defs") - set(input_files "") + if(NOT IOS) + set(def_relative_files "exc.defs" "mach_exc.defs" "notify.defs") + set(input_files "${CMAKE_CURRENT_LIST_DIR}/mach/child_port.defs") + elseif() + set(def_relative_files "") + set(input_files + "${CMAKE_CURRENT_LIST_DIR}/../third_party/xnu/osfmk/mach/exc.defs" + "${CMAKE_CURRENT_LIST_DIR}/../third_party/xnu/osfmk/mach/mach_exc.defs" + ) + endif() foreach(x ${def_relative_files}) # CMAKE_OSX_SYSROOT may be empty (e.g. for Makefile generators), # in this case files will be taken from root. @@ -322,7 +347,6 @@ if(APPLE) endif() list(APPEND input_files "${full_path}") endforeach() - list(APPEND input_files "${CMAKE_CURRENT_LIST_DIR}/mach/child_port.defs") find_package(PythonInterp 2.7 REQUIRED) From 24a338862007de81a003e7187b1f2c44378140a0 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 26 Jun 2020 16:04:12 +0200 Subject: [PATCH 026/146] feat: Rework attachment support patch --- client/crash_report_database.cc | 10 +-- client/crash_report_database.h | 4 +- client/crash_report_database_generic.cc | 8 ++ client/crash_report_database_mac.mm | 8 ++ client/crash_report_database_test.cc | 29 +++---- client/crash_report_database_win.cc | 75 ----------------- client/crashpad_client.h | 13 --- client/crashpad_client_linux.cc | 25 ------ client/crashpad_client_mac.cc | 47 ++++------- client/crashpad_client_win.cc | 27 ------- handler/handler_main.cc | 81 +++++-------------- .../linux/crash_report_exception_handler.cc | 17 ---- .../linux/crash_report_exception_handler.h | 1 - handler/mac/crash_report_exception_handler.cc | 36 +++++---- handler/mac/crash_report_exception_handler.h | 10 +-- handler/win/crash_report_exception_handler.h | 1 - 16 files changed, 90 insertions(+), 302 deletions(-) diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc index 65b0c31d61..e1559ca738 100644 --- a/client/crash_report_database.cc +++ b/client/crash_report_database.cc @@ -18,14 +18,6 @@ namespace crashpad { -bool CrashReportDatabase::AttachmentNameIsOK(const std::string& name) { - for (const char c : name) { - if (c != '_' && c != '-' && c != '.' && !isalnum(c)) - return false; - } - return true; -} - CrashReportDatabase::Report::Report() : uuid(), file_path(), @@ -95,7 +87,7 @@ CrashReportDatabase::UploadReport::~UploadReport() { } } -bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath& path, +bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath path, CrashReportDatabase* db) { database_ = db; InitializeAttachments(); diff --git a/client/crash_report_database.h b/client/crash_report_database.h index 8fb294b519..ce317f7972 100644 --- a/client/crash_report_database.h +++ b/client/crash_report_database.h @@ -181,7 +181,7 @@ class CrashReportDatabase { friend class CrashReportDatabaseMac; friend class CrashReportDatabaseWin; - bool Initialize(const base::FilePath& path, CrashReportDatabase* database); + bool Initialize(const base::FilePath path, CrashReportDatabase* database); void InitializeAttachments(); std::unique_ptr reader_; @@ -406,8 +406,6 @@ class CrashReportDatabase { protected: CrashReportDatabase() {} - static bool AttachmentNameIsOK(const std::string& name); - 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 diff --git a/client/crash_report_database_generic.cc b/client/crash_report_database_generic.cc index 376c4d1a6a..aeaf2af43f 100644 --- a/client/crash_report_database_generic.cc +++ b/client/crash_report_database_generic.cc @@ -44,6 +44,14 @@ UUID UUIDFromReportPath(const base::FilePath& path) { return uuid; } +bool AttachmentNameIsOK(const std::string& name) { + for (const char c : name) { + if (c != '_' && c != '-' && c != '.' && !isalnum(c)) + return false; + } + return true; +} + using OperationStatus = CrashReportDatabase::OperationStatus; constexpr base::FilePath::CharType kSettings[] = diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index 57891d4bb6..d3a366bde8 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -71,6 +71,14 @@ constexpr char kXattrDatabaseInitialized[] = "initialized"; +bool AttachmentNameIsOK(const std::string& name) { + for (const char c : name) { + if (c != '_' && c != '-' && c != '.' && !isalnum(c)) + return false; + } + return true; +} + // 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. diff --git a/client/crash_report_database_test.cc b/client/crash_report_database_test.cc index a4541512e7..20512a4509 100644 --- a/client/crash_report_database_test.cc +++ b/client/crash_report_database_test.cc @@ -674,6 +674,10 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) { } TEST_F(CrashReportDatabaseTest, Attachments) { +#if defined(OS_MACOSX) || defined(OS_WIN) + // Attachments aren't supported on Mac and Windows yet. + GTEST_SKIP(); +#else std::unique_ptr new_report; ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); @@ -712,9 +716,16 @@ TEST_F(CrashReportDatabaseTest, Attachments) { 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); +#endif } TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { +#if defined(OS_MACOSX) || defined(OS_WIN) + // Attachments aren't supported on Mac and Windows yet. + GTEST_SKIP(); +#else + // TODO: This is using paths that are specific to the generic implementation + // and will need to be generalized for Mac and Windows. std::unique_ptr new_report; ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), CrashReportDatabase::kNoError); @@ -736,27 +747,16 @@ TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { ASSERT_TRUE(LoggingRemoveFile(report.file_path)); -// Additional check for Generic database -#if !defined(OS_MACOSX) && !defined(OS_WIN) ASSERT_TRUE(LoggingRemoveFile(base::FilePath( report.file_path.RemoveFinalExtension().value() + ".meta"))); -#endif ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), CrashReportDatabase::kReportNotFound); -#ifdef OS_WIN - auto uuid_str = uuid.ToString16(); -#else - auto uuid_str = uuid.ToString(); -#endif - base::FilePath report_attachments_dir( - path().Append(FILE_PATH_LITERAL("attachments")).Append(uuid_str)); - base::FilePath file_path1( - report_attachments_dir.Append(FILE_PATH_LITERAL("file1"))); - base::FilePath file_path2( - report_attachments_dir.Append(FILE_PATH_LITERAL("file2"))); + path().Append("attachments").Append(uuid.ToString())); + base::FilePath file_path1(report_attachments_dir.Append("file1")); + base::FilePath file_path2(report_attachments_dir.Append("file2")); EXPECT_TRUE(FileExists(file_path1)); EXPECT_TRUE(FileExists(file_path1)); @@ -765,6 +765,7 @@ TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { EXPECT_FALSE(FileExists(file_path1)); EXPECT_FALSE(FileExists(file_path2)); EXPECT_FALSE(FileExists(report_attachments_dir)); +#endif } // This test uses knowledge of the database format to break it, so it only diff --git a/client/crash_report_database_win.cc b/client/crash_report_database_win.cc index 51a8472848..f1a4960851 100644 --- a/client/crash_report_database_win.cc +++ b/client/crash_report_database_win.cc @@ -41,7 +41,6 @@ namespace { constexpr wchar_t kReportsDirectory[] = L"reports"; constexpr wchar_t kMetadataFileName[] = L"metadata"; -constexpr wchar_t kAttachmentsDirectory[] = L"attachments"; constexpr wchar_t kSettings[] = L"settings.dat"; @@ -664,16 +663,6 @@ class CrashReportDatabaseWin : public CrashReportDatabase { std::unique_ptr AcquireMetadata(); - //! \brief Cleans any attachments that have no associated report. - void CleanOrphanedAttachments(); - - //! \brief Attempt 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 report identifier which attachments to remove. - void RemoveAttachmentsByUUID(const UUID& uuid); - base::FilePath base_dir_; Settings settings_; InitializationStateDcheck initialized_; @@ -692,11 +681,6 @@ base::FilePath CrashReportDatabaseWin::AttachmentsPath(const UUID& uuid) { FileWriter* CrashReportDatabase::NewReport::AddAttachment( const std::string& name) { - if (!AttachmentNameIsOK(name)) { - LOG(ERROR) << "invalid name for attachment " << name; - return nullptr; - } - auto database_win = static_cast(database_); base::FilePath attachments_root_dir = database_win->AttachmentsRootPath(); base::FilePath attachments_dir = database_win->AttachmentsPath(uuid_); @@ -769,9 +753,6 @@ bool CrashReportDatabaseWin::Initialize(bool may_create) { if (!CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory))) return false; - if (!CreateDirectoryIfNecessary(base_dir_.Append(kAttachmentsDirectory))) - return false; - if (!settings_.Initialize(base_dir_.Append(kSettings))) return false; @@ -1000,62 +981,6 @@ std::unique_ptr CrashReportDatabaseWin::AcquireMetadata() { return Metadata::Create(metadata_file, base_dir_.Append(kReportsDirectory)); } -void CrashReportDatabaseWin::CleanOrphanedAttachments() { - base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory)); - DirectoryReader reader; - if (!reader.Open(root_attachments_dir)) { - LOG(ERROR) << "no attachments dir"; - return; - } - - std::unique_ptr metadata(AcquireMetadata()); - if (!metadata) - return; - - base::FilePath filename; - DirectoryReader::Result result; - 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 " - << base::UTF16ToUTF8(filename.value()); - continue; - } - - // Check to see if the report exist. - const ReportDisk* report_disk; - const OperationStatus os = metadata->FindSingleReport(uuid, &report_disk); - if (os != OperationStatus::kReportNotFound) { - continue; - } - - // Couldn't find a report, assume these attachments are orphaned. - RemoveAttachmentsByUUID(uuid); - } - } -} - -void CrashReportDatabaseWin::RemoveAttachmentsByUUID(const UUID &uuid) { - base::FilePath attachments_dir = AttachmentsPath(uuid); - DirectoryReader reader; - if (!reader.Open(attachments_dir)) { - return; - } - - base::FilePath filename; - DirectoryReader::Result result; - while ((result = reader.NextFile(&filename)) == - DirectoryReader::Result::kSuccess) { - const base::FilePath filepath(attachments_dir.Append(filename)); - LoggingRemoveFile(filepath); - } - - LoggingRemoveDirectory(attachments_dir); -} - std::unique_ptr InitializeInternal( const base::FilePath& path, bool may_create) { diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 256d7f0b70..6f8c2fe018 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -121,19 +121,6 @@ class CrashpadClient { bool asynchronous_start, const std::vector& attachments = {}); -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) - bool StartHandlerWithAttachments( - const base::FilePath& handler, - const base::FilePath& database, - const base::FilePath& metrics_dir, - const std::string& url, - const std::map& annotations, - const std::map& fileAttachments, - const std::vector& arguments, - bool restartable, - bool asynchronous_start); -#endif // OS_WIN || OS_MACOSX || OS_LINUX - #if defined(OS_ANDROID) || defined(OS_LINUX) || DOXYGEN //! \brief Retrieve the socket and process ID for the handler. //! diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index 819db97e9b..4fb99d846f 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -402,31 +402,6 @@ bool CrashpadClient::StartHandler( std::move(client_sock), handler_pid, &unhandled_signals_); } -bool CrashpadClient::StartHandlerWithAttachments( - const base::FilePath& handler, - const base::FilePath& database, - const base::FilePath& metrics_dir, - const std::string& url, - const std::map& annotations, - const std::map& fileAttachments, - const std::vector& arguments, - bool restartable, - bool asynchronous_start) { - std::vector updated_arguments = arguments; - for (const auto& kv: fileAttachments) { - std::string attachmentArg = "--attachment=" + kv.first + "=" + kv.second.value(); - updated_arguments.push_back(attachmentArg); - } - - // FIXME this is not the same as calling StartHandler on e.g. Mac - return CrashpadClient::StartHandlerAtCrash(handler, - database, - metrics_dir, - url, - annotations, - updated_arguments); -} - #if defined(OS_ANDROID) || defined(OS_LINUX) // static bool CrashpadClient::GetHandlerSocket(int* sock, pid_t* pid) { diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index 70c9469eef..13da5a46d4 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -123,6 +123,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { 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)); @@ -162,6 +163,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { url, annotations, arguments, + attachments, std::move(receive_right), handler_restarter.get(), false)) { @@ -170,7 +172,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { if (handler_restarter && handler_restarter->StartRestartThread( - handler, database, metrics_dir, url, annotations, arguments)) { + handler, database, metrics_dir, url, annotations, arguments, attachments)) { // The thread owns the object now. ignore_result(handler_restarter.release()); } @@ -205,6 +207,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { url_, annotations_, arguments_, + attachments_, base::mac::ScopedMachReceiveRight(rights), this, true); @@ -221,6 +224,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { url_(), annotations_(), arguments_(), + attachments_(), notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), last_start_time_(0) { } @@ -250,6 +254,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { 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) { @@ -330,6 +335,11 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { 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, @@ -365,13 +375,15 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { const base::FilePath& metrics_dir, const std::string& url, const std::map& annotations, - const std::vector& arguments) { + 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); @@ -424,6 +436,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { std::string url_; std::map annotations_; std::vector arguments_; + std::vector attachments_; base::mac::ScopedMachReceiveRight notify_port_; uint64_t last_start_time_; @@ -448,9 +461,6 @@ bool CrashpadClient::StartHandler( bool restartable, bool asynchronous_start, const std::vector& attachments) { - // Attachments are not implemented on MacOS yet. - DCHECK(attachments.empty()); - // 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. @@ -461,6 +471,7 @@ bool CrashpadClient::StartHandler( url, annotations, arguments, + attachments, restartable && MacOSXMinorVersion() >= 10)); if (!exception_port.is_valid()) { return false; @@ -470,32 +481,6 @@ bool CrashpadClient::StartHandler( return true; } -bool CrashpadClient::StartHandlerWithAttachments( - const base::FilePath& handler, - const base::FilePath& database, - const base::FilePath& metrics_dir, - const std::string& url, - const std::map& annotations, - const std::map& fileAttachments, - const std::vector& arguments, - bool restartable, - bool asynchronous_start) { - std::vector updated_arguments = arguments; - for (const auto& kv: fileAttachments) { - std::string attachmentArg = "--attachment=" + kv.first + "=" + kv.second.value(); - updated_arguments.push_back(attachmentArg); - } - - return StartHandler(handler, - database, - metrics_dir, - url, - annotations, - updated_arguments, - restartable, - asynchronous_start); -} - bool CrashpadClient::SetHandlerMachService(const std::string& service_name) { base::mac::ScopedMachSendRight exception_port(BootstrapLookUp(service_name)); if (!exception_port.is_valid()) { diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index a749c9ba5a..1f2fb819d0 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -669,33 +669,6 @@ bool CrashpadClient::StartHandler( } } -bool CrashpadClient::StartHandlerWithAttachments( - const base::FilePath& handler, - const base::FilePath& database, - const base::FilePath& metrics_dir, - const std::string& url, - const std::map& annotations, - const std::map& fileAttachments, - const std::vector& arguments, - bool restartable, - bool asynchronous_start) { - std::vector updated_arguments = arguments; - for (const auto& kv : fileAttachments) { - std::string attachmentArg = - "--attachment=" + kv.first + "=" + base::UTF16ToUTF8(kv.second.value()); - updated_arguments.push_back(attachmentArg); - } - - return StartHandler(handler, - database, - metrics_dir, - url, - annotations, - updated_arguments, - restartable, - asynchronous_start); -} - bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) { DCHECK(ipc_pipe_.empty()); DCHECK(!ipc_pipe.empty()); diff --git a/handler/handler_main.cc b/handler/handler_main.cc index b91f10d58d..83dbd75e86 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -101,7 +101,7 @@ void Usage(const base::FilePath& me) { "Crashpad's exception handler server.\n" "\n" " --annotation=KEY=VALUE set a process annotation in each crash report\n" -#if defined(OS_WIN) || defined(OS_LINUX) +#if !defined(OS_FUCHSIA) " --attachment=FILE_PATH attach specified file to each crash report\n" " at the time of the crash\n" #endif // OS_WIN || OS_LINUX @@ -160,9 +160,6 @@ void Usage(const base::FilePath& me) { #endif // OS_LINUX || OS_ANDROID " --url=URL send crash reports to this Breakpad server URL,\n" " only if uploads are enabled for the database\n" -#if !defined(OS_FUCHSIA) -" --attachment=NAME=PATH attach a copy of a file, along with a crash dump\n" -#endif #if defined(OS_CHROMEOS) " --use-cros-crash-reporter\n" " pass crash reports to /sbin/crash_reporter\n" @@ -187,7 +184,6 @@ void Usage(const base::FilePath& me) { struct Options { std::map annotations; std::map monitor_self_annotations; - std::map attachments; std::string url; base::FilePath database; base::FilePath metrics_dir; @@ -219,9 +215,9 @@ struct Options { base::FilePath minidump_dir_for_tests; bool always_allow_feedback = false; #endif // OS_CHROMEOS -#if defined(OS_WIN) || defined (OS_LINUX) +#if !defined(OS_FUCHSIA) std::vector attachments; -#endif // OS_WIN || OS_LINUX +#endif // !OS_FUCHSIA }; // Splits |key_value| on '=' and inserts the resulting key and value into |map|. @@ -246,30 +242,6 @@ bool AddKeyValueToMap(std::map* map, } return true; } -// Overloaded version, to accept base::FilePath as a VALUE. -bool AddKeyValueToMap(std::map* map, - const std::string& key_value, - const char* argument) { - std::string key; - std::string raw_value; - if (!SplitStringFirst(key_value, '=', &key, &raw_value)) { - LOG(ERROR) << argument << " requires NAME=PATH"; - return false; - } - -#ifdef OS_WIN - base::FilePath value(base::UTF8ToUTF16(raw_value)); -#else - base::FilePath value(raw_value); -#endif - - base::FilePath old_value; - if (!MapInsertOrReplace(map, key, value, &old_value)) { - LOG(WARNING) << argument << " has duplicate name " << key - << ", discarding value " << old_value.value().c_str(); - } - return true; -} // Calls Metrics::HandlerLifetimeMilestone, but only on the first call. This is // to prevent multiple exit events from inadvertently being recorded, which @@ -551,9 +523,9 @@ int HandlerMain(int argc, // Long options without short equivalents. kOptionLastChar = 255, kOptionAnnotation, -#if defined(OS_WIN) || defined(OS_LINUX) +#if !defined(OS_FUCHSIA) kOptionAttachment, -#endif // OS_WIN || OS_LINUX +#endif // !OS_FUCHSIA kOptionDatabase, #if defined(OS_MACOSX) kOptionHandshakeFD, @@ -590,9 +562,6 @@ int HandlerMain(int argc, kOptionTraceParentWithException, #endif kOptionURL, -#if !defined(OS_FUCHSIA) - kOptionAttachment, -#endif #if defined(OS_CHROMEOS) kOptionUseCrosCrashReporter, kOptionMinidumpDirForTests, @@ -609,9 +578,9 @@ int HandlerMain(int argc, static constexpr option long_options[] = { {"annotation", required_argument, nullptr, kOptionAnnotation}, -#if defined(OS_WIN) || defined(OS_LINUX) +#if !defined(OS_FUCHSIA) {"attachment", required_argument, nullptr, kOptionAttachment}, -#endif // OS_WIN || OS_LINUX +#endif // !OS_FUCHSIA {"database", required_argument, nullptr, kOptionDatabase}, #if defined(OS_MACOSX) {"handshake-fd", required_argument, nullptr, kOptionHandshakeFD}, @@ -675,22 +644,16 @@ int HandlerMain(int argc, kOptionTraceParentWithException}, #endif // OS_LINUX || OS_ANDROID {"url", required_argument, nullptr, kOptionURL}, -#if !defined(OS_FUCHSIA) - {"attachment", required_argument, nullptr, kOptionAttachment}, -#endif #if defined(OS_CHROMEOS) {"use-cros-crash-reporter", - no_argument, - nullptr, - kOptionUseCrosCrashReporter}, + no_argument, + nullptr, + kOptionUseCrosCrashReporter}, {"minidump-dir-for-tests", - required_argument, - nullptr, - kOptionMinidumpDirForTests}, - {"always-allow-feedback", - no_argument, - nullptr, - kOptionAlwaysAllowFeedback}, + required_argument, + nullptr, + kOptionMinidumpDirForTests}, + {"always-allow-feedback", no_argument, nullptr, kOptionAlwaysAllowFeedback}, #endif // OS_CHROMEOS #if defined(OS_ANDROID) {"write-minidump-to-log", no_argument, nullptr, kOptionWriteMinidumpToLog}, @@ -724,13 +687,13 @@ int HandlerMain(int argc, } break; } -#if defined(OS_WIN) || defined(OS_LINUX) +#if !defined(OS_FUCHSIA) case kOptionAttachment: { options.attachments.push_back(base::FilePath( ToolSupport::CommandLineArgumentToFilePathStringType(optarg))); break; } -#endif // OS_WIN || OS_LINUX +#endif // !OS_FUCHSIA case kOptionDatabase: { options.database = base::FilePath( ToolSupport::CommandLineArgumentToFilePathStringType(optarg)); @@ -852,14 +815,6 @@ int HandlerMain(int argc, options.url = optarg; break; } -#if !defined(OS_FUCHSIA) - case kOptionAttachment: { - if (!AddKeyValueToMap(&options.attachments, optarg, "--attachment")) { - return ExitFailure(); - } - break; - } -#endif #if defined(OS_CHROMEOS) case kOptionUseCrosCrashReporter: { options.use_cros_crash_reporter = true; @@ -1050,9 +1005,9 @@ int HandlerMain(int argc, database.get(), static_cast(upload_thread.Get()), &options.annotations, -#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_MACOSX) +#if !defined(OS_FUCHSIA) &options.attachments, -#endif // OS_WIN || OS_LINUX || OS_MACOSX +#endif // !OS_FUCHSIA #if defined(OS_ANDROID) options.write_minidump_to_database, options.write_minidump_to_log, diff --git a/handler/linux/crash_report_exception_handler.cc b/handler/linux/crash_report_exception_handler.cc index ac2a66d734..d6e54be7dd 100644 --- a/handler/linux/crash_report_exception_handler.cc +++ b/handler/linux/crash_report_exception_handler.cc @@ -193,23 +193,6 @@ bool CrashReportExceptionHandler::WriteMinidumpToDatabase( return false; } - if (process_attachments_) { - // Note that attachments are read at this point each time rather than once - // so that if the contents of the file has changed it will be re-read for - // each upload (e.g. in the case of a log file). - for (const auto& it : *process_attachments_) { - FileWriter* writer = new_report->AddAttachment(it.first); - if (writer) { - std::string contents; - if (!LoggingReadEntireFile(it.second, &contents)) { - // Not being able to read the file isn't considered fatal, and - // should not prevent the report from being processed. - continue; - } - writer->Write(contents.data(), contents.size()); - } - } - } bool write_minidump_to_log_succeed = false; if (write_minidump_to_log) { if (auto* file_reader = new_report->Reader()) { diff --git a/handler/linux/crash_report_exception_handler.h b/handler/linux/crash_report_exception_handler.h index 69cf276b4e..ae28430171 100644 --- a/handler/linux/crash_report_exception_handler.h +++ b/handler/linux/crash_report_exception_handler.h @@ -18,7 +18,6 @@ #include #include -#include "base/files/file_path.h" #include "base/macros.h" #include "client/crash_report_database.h" #include "handler/crash_report_upload_thread.h" diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index fa620d741c..5113e1f4fb 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -27,6 +27,7 @@ #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" @@ -46,12 +47,12 @@ CrashReportExceptionHandler::CrashReportExceptionHandler( CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, - const std::map* process_attachments, + const std::vector* attachments, const UserStreamDataSources* user_stream_data_sources) : database_(database), upload_thread_(upload_thread), process_annotations_(process_annotations), - process_attachments_(process_attachments), + attachments_(attachments), user_stream_data_sources_(user_stream_data_sources) {} CrashReportExceptionHandler::~CrashReportExceptionHandler() { @@ -181,22 +182,23 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( return KERN_FAILURE; } - if (process_attachments_) { - // Note that attachments are read at this point each time rather than once - // so that if the contents of the file has changed it will be re-read for - // each upload (e.g. in the case of a log file). - for (const auto& it : *process_attachments_) { - FileWriter* writer = new_report->AddAttachment(it.first); - if (writer) { - std::string contents; - if (!LoggingReadEntireFile(it.second, &contents)) { - // Not being able to read the file isn't considered fatal, and - // should not prevent the report from being processed. - continue; - } - writer->Write(contents.data(), contents.size()); - } + 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; diff --git a/handler/mac/crash_report_exception_handler.h b/handler/mac/crash_report_exception_handler.h index a093603fe5..7df8e70091 100644 --- a/handler/mac/crash_report_exception_handler.h +++ b/handler/mac/crash_report_exception_handler.h @@ -50,10 +50,8 @@ class CrashReportExceptionHandler final //! To interoperate with Breakpad servers, the recommended practice is to //! specify values for the `"prod"` and `"ver"` keys as process //! annotations. - //! \param[in] process_attachments A map of file name keys to file paths to be - //! included in the report. Each time a report is written, the file paths - //! will be read in their entirety and included in the report using the - //! file name key as the name in the http upload. + //! \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 @@ -62,7 +60,7 @@ class CrashReportExceptionHandler final CrashReportDatabase* database, CrashReportUploadThread* upload_thread, const std::map* process_annotations, - const std::map* process_attachments, + const std::vector* attachments, const UserStreamDataSources* user_stream_data_sources); ~CrashReportExceptionHandler(); @@ -91,7 +89,7 @@ class CrashReportExceptionHandler final CrashReportDatabase* database_; // weak CrashReportUploadThread* upload_thread_; // weak const std::map* process_annotations_; // weak - const std::map* process_attachments_; // weak + const std::vector* attachments_; // weak const UserStreamDataSources* user_stream_data_sources_; // weak DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler); diff --git a/handler/win/crash_report_exception_handler.h b/handler/win/crash_report_exception_handler.h index 9fe61ec64a..cefb898194 100644 --- a/handler/win/crash_report_exception_handler.h +++ b/handler/win/crash_report_exception_handler.h @@ -20,7 +20,6 @@ #include #include -#include "base/files/file_path.h" #include "base/macros.h" #include "handler/user_stream_data_source.h" #include "util/win/exception_handler_server.h" From e778e67793ab5437ab1d09d7a35d25dffdb0f011 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 26 Jun 2020 16:57:30 +0200 Subject: [PATCH 027/146] fix: Fix broken ifdef upstream --- snapshot/crashpad_types/crashpad_info_reader.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snapshot/crashpad_types/crashpad_info_reader.cc b/snapshot/crashpad_types/crashpad_info_reader.cc index e4c8b93403..5f2f35601f 100644 --- a/snapshot/crashpad_types/crashpad_info_reader.cc +++ b/snapshot/crashpad_types/crashpad_info_reader.cc @@ -20,7 +20,7 @@ #include "client/crashpad_info.h" #include "util/misc/as_underlying_type.h" -#if defined(OS_WINDOWS) +#if defined(OS_WIN) #include "util/win/traits.h" #elif defined(OS_LINUX) || defined(OS_ANDROID) #include "util/linux/traits.h" From c4d5234f9024763094d7b978b246c2d4dbf54748 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 1 Jul 2020 12:04:44 +0200 Subject: [PATCH 028/146] build crashpad with openssl if possible --- CMakeLists.txt | 15 +++++++++++---- util/CMakeLists.txt | 21 ++++++++++++++------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b9ca022c7..abb06971a6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ 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) @@ -20,6 +24,13 @@ 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") @@ -38,10 +49,6 @@ function(crashpad_install_dev) endif() endfunction() -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - set(LINUX TRUE) -endif() - if(WIN32) enable_language(ASM_MASM) if(MINGW) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 14f3903131..68b9633910 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -400,15 +400,20 @@ if(LINUX) target_link_libraries(crashpad_util PRIVATE pthread) endif() +if(CRASHPAD_USE_BORINGSSL) + target_compile_definitions(crashpad_util PRIVATE CRASHPAD_USE_BORINGSSL) + target_link_libraries(crashpad_util PRIVATE OpenSSL::SSL OpenSSL::Crypto) +endif() + if(WIN32) target_link_libraries(crashpad_util PRIVATE user32 version winhttp) if(MSVC) target_compile_options(crashpad_util PRIVATE "/wd4201") elseif(MINGW) - target_compile_options(crashpad_util PRIVATE + target_compile_options(crashpad_util PRIVATE $<$:-municode> ) - target_compile_definitions(crashpad_util PRIVATE + target_compile_definitions(crashpad_util PRIVATE "__STDC_VERSION__=199901L" $<$:__MINGW32__> ) @@ -420,13 +425,15 @@ if(WIN32) "-include memory" ) endif() - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(crashpad_util PRIVATE - $<$:-Wno-multichar> - ) - endif() endif() +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_util PRIVATE + $<$:-Wno-multichar> + ) +endif() + + set_property(TARGET crashpad_util PROPERTY EXPORT_NAME util) add_library(crashpad::util ALIAS crashpad_util) From f24aa826f4f7e9f90a41b028b5009d0f5b9cbe7d Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 1 Jul 2020 15:49:32 +0200 Subject: [PATCH 029/146] fix: Add a Host Header to the upload request (#17) This fixes https minidump uploads on linux. --- util/net/http_transport_socket.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/util/net/http_transport_socket.cc b/util/net/http_transport_socket.cc index b9c6c9c5e0..d1c0194c18 100644 --- a/util/net/http_transport_socket.cc +++ b/util/net/http_transport_socket.cc @@ -328,11 +328,15 @@ base::ScopedFD CreateSocket(const std::string& hostname, bool WriteRequest(Stream* stream, const std::string& method, + const std::string& hostname, const std::string& resource, const HTTPHeaders& headers, HTTPBodyStream* body_stream) { - std::string request_line = base::StringPrintf( - "%s %s HTTP/1.0\r\n", method.c_str(), resource.c_str()); + std::string request_line = + base::StringPrintf("%s %s HTTP/1.0\r\nHost: %s\r\n", + method.c_str(), + resource.c_str(), + hostname.c_str()); if (!stream->LoggingWrite(request_line.data(), request_line.size())) return false; @@ -574,8 +578,12 @@ bool HTTPTransportSocket::ExecuteSynchronously(std::string* response_body) { std::unique_ptr stream(std::make_unique(sock.get())); #endif // CRASHPAD_USE_BORINGSSL - if (!WriteRequest( - stream.get(), method(), resource, headers(), body_stream())) { + if (!WriteRequest(stream.get(), + method(), + hostname, + resource, + headers(), + body_stream())) { return false; } From e6bcc27ffdeda51cb9f03f0bb117ac204c2c70d9 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 15 Jul 2020 12:38:33 +0200 Subject: [PATCH 030/146] ci: Run a simple CMake build on CI (#18) --- .github/workflows/build.yml | 23 +++++++++++++++++++++++ client/CMakeLists.txt | 11 ++++++++--- handler/CMakeLists.txt | 13 +++++++------ minidump/CMakeLists.txt | 4 ++-- snapshot/CMakeLists.txt | 15 ++++++++++----- 5 files changed, 50 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..3dd3c92123 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,23 @@ +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" + - uses: lukka/run-cmake@v2 + with: + cmakeListsOrSettingsJson: "CMakeListsTxtAdvanced" + buildWithCMakeArgs: "--parallel" diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index b1a77c1944..11ba9f4edc 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -79,12 +79,11 @@ if(WIN32) ) endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(crashpad_client PRIVATE - "-Wno-multichar" + target_compile_options(crashpad_client PRIVATE "-Wno-attributes" ) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_compile_options(crashpad_client PRIVATE + target_compile_options(crashpad_client PRIVATE "-Wno-unknown-attributes" "-Wno-unknown-pragmas" ) @@ -99,6 +98,12 @@ if(IOS) ) 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" diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index a0b5d2deeb..4d37ba936a 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -62,11 +62,12 @@ if(WIN32) if(MSVC) target_compile_options(crashpad_handler_lib PRIVATE "/wd4201") endif() - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(crashpad_handler_lib PRIVATE - "-Wno-multichar" - ) - 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) @@ -105,4 +106,4 @@ if(NOT IOS) install(TARGETS crashpad_handler EXPORT crashpad_export RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) -endif() \ No newline at end of file +endif() diff --git a/minidump/CMakeLists.txt b/minidump/CMakeLists.txt index 83ed22f121..1bf972ab98 100644 --- a/minidump/CMakeLists.txt +++ b/minidump/CMakeLists.txt @@ -66,8 +66,8 @@ if(MSVC) target_compile_options(crashpad_minidump PRIVATE "/wd4201" "/wd4324") endif() -if(WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(crashpad_minidump PRIVATE +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_minidump PRIVATE "-Wno-multichar" ) endif() diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index bc8ec7cf2c..5da894b76a 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -205,17 +205,22 @@ if(WIN32) target_compile_options(crashpad_snapshot PRIVATE "/wd4201") endif() if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - target_compile_options(crashpad_snapshot PRIVATE - "-Wno-multichar" + target_compile_options(crashpad_snapshot PRIVATE "-Wno-attributes" ) elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_compile_options(crashpad_snapshot PRIVATE - "-Wno-unknown-attributes" - ) + target_compile_options(crashpad_snapshot PRIVATE + "-Wno-unknown-attributes" + ) endif() 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) From 03abac256cde53f2c079097510edfb3bb275549b Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 21 Jul 2020 19:09:52 +0200 Subject: [PATCH 031/146] feat: Implement a FirstChanceHandler for Windows as well (#20) --- client/crashpad_client.h | 25 +++++++++++++++++++++++-- client/crashpad_client_linux.cc | 6 +++--- client/crashpad_client_win.cc | 12 ++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 6f8c2fe018..f9b8aa84dd 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -398,7 +398,7 @@ class CrashpadClient { static void CrashWithoutDump(const std::string& message); //! \brief The type for custom handlers installed by clients. - using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*); + using FirstChanceHandlerLinux = bool (*)(int, siginfo_t*, ucontext_t*); //! \brief Installs a custom crash signal handler which runs before the //! currently installed Crashpad handler. @@ -416,7 +416,7 @@ class CrashpadClient { //! signal handler is run. //! //! \param[in] handler The custom crash signal handler to install. - static void SetFirstChanceExceptionHandler(FirstChanceHandler handler); + static void SetFirstChanceExceptionHandler(FirstChanceHandlerLinux handler); //! \brief Configures a set of signals that shouldn't have Crashpad signal //! handlers installed. @@ -501,6 +501,27 @@ class CrashpadClient { #endif #if defined(OS_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 diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index 4fb99d846f..af4c7969a6 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -132,7 +132,7 @@ class SignalHandler { // handler will be restored and the signal reraised. static void DisableForThread() { disabled_for_thread_ = true; } - void SetFirstChanceHandler(CrashpadClient::FirstChanceHandler handler) { + void SetFirstChanceHandler(CrashpadClient::FirstChanceHandlerLinux handler) { first_chance_handler_ = handler; } @@ -193,7 +193,7 @@ class SignalHandler { Signals::OldActions old_actions_ = {}; ExceptionInformation exception_information_ = {}; - CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr; + CrashpadClient::FirstChanceHandlerLinux first_chance_handler_ = nullptr; static SignalHandler* handler_; @@ -566,7 +566,7 @@ void CrashpadClient::CrashWithoutDump(const std::string& message) { // static void CrashpadClient::SetFirstChanceExceptionHandler( - FirstChanceHandler handler) { + FirstChanceHandlerLinux handler) { DCHECK(SignalHandler::Get()); SignalHandler::Get()->SetFirstChanceHandler(handler); } diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 1f2fb819d0..486db54934 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -68,6 +68,8 @@ ExceptionInformation g_crash_exception_information; 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; @@ -134,6 +136,10 @@ LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { 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. @@ -1070,4 +1076,10 @@ bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, return result; } +// static +void CrashpadClient::SetFirstChanceExceptionHandler( + FirstChanceHandlerWin handler) { + first_chance_handler_ = handler; +} + } // namespace crashpad From da9a6485b8fd7c9bc5dade452c2f50cd93153cda Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 3 Aug 2020 09:50:00 +0200 Subject: [PATCH 032/146] build: Update submodule and linux handler --- handler/CMakeLists.txt | 6 ++++++ third_party/mini_chromium/mini_chromium | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 4d37ba936a..181efa510e 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -80,6 +80,12 @@ if(NOT IOS) main.cc ) + if(LINUX) + target_sources(crashpad_handler PRIVATE + ../client/pthread_create_linux.cc + ) + endif() + target_link_libraries(crashpad_handler PRIVATE $ diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 4e8ff0c3a2..891e31d0b6 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 4e8ff0c3a29fe33b7b99bcd02ca3b53fa98143ac +Subproject commit 891e31d0b649ec9fa98ca5745f9dcd14a34de34c From a0b37e180de96fb1dd22a26ce1bc847f4dd21824 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 2 Nov 2020 16:24:18 +0100 Subject: [PATCH 033/146] feat: CMake build support for iOS (#27) --- .github/workflows/build.yml | 10 ++++++++++ handler/CMakeLists.txt | 2 +- util/CMakeLists.txt | 23 +++++++++++++++++++++-- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3dd3c92123..a5211b0776 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,3 +21,13 @@ jobs: with: cmakeListsOrSettingsJson: "CMakeListsTxtAdvanced" buildWithCMakeArgs: "--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/handler/CMakeLists.txt b/handler/CMakeLists.txt index 181efa510e..3288ac46b2 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -11,7 +11,7 @@ add_library(crashpad_handler_lib STATIC user_stream_data_source.h ) -if(APPLE) +if(APPLE AND NOT IOS) target_sources(crashpad_handler_lib PRIVATE mac/crash_report_exception_handler.cc mac/crash_report_exception_handler.h diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 90cc5c241a..7a3425f8da 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -334,7 +334,7 @@ if(APPLE) if(NOT IOS) set(def_relative_files "exc.defs" "mach_exc.defs" "notify.defs") set(input_files "${CMAKE_CURRENT_LIST_DIR}/mach/child_port.defs") - elseif() + else() set(def_relative_files "") set(input_files "${CMAKE_CURRENT_LIST_DIR}/../third_party/xnu/osfmk/mach/exc.defs" @@ -356,6 +356,25 @@ if(APPLE) set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/util/mach") file(MAKE_DIRECTORY "${output_dir}") + get_property(archs TARGET crashpad_util PROPERTY OSX_ARCHITECTURES) + if(NOT archs) + if(IOS) + set(archs "arm64") + else() + set(archs "x86_64") + endif() + endif() + list(TRANSFORM archs PREPEND "--arch=") + + set(includes + "${CMAKE_CURRENT_SOURCE_DIR}/.." + "${CMAKE_CURRENT_SOURCE_DIR}/../compat/mac" + ) + if(IOS) + list(APPEND includes "${CMAKE_CURRENT_SOURCE_DIR}/../compat/ios") + endif() + list(TRANSFORM includes PREPEND "--include=") + # Create generate rule for each input file. Add each generated output # as a source to the target. foreach(input ${input_files}) @@ -368,7 +387,7 @@ if(APPLE) OUTPUT ${output_files} COMMAND - "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" ${output_files} + "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" ${archs} ${includes} "${input}" ${output_files} DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" ) From fba97d0d5556ebd2dc94d183304ff990d2462820 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 4 Nov 2020 11:41:27 +0100 Subject: [PATCH 034/146] fix: Silence new MSVC warning in zlib (#28) --- third_party/zlib/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 7ed2f58680..451433171f 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -73,6 +73,7 @@ else() "/wd4267" # conversion from 'size_t' to 't', possible loss of data "/wd4324" # structure was padded due to alignment specifier "/wd4702" # unreachable code + "/wd5105" # see https://github.com/getsentry/sentry-native/issues/415 ) endif() endif() From 4e586713187add7ac504e157bf2ccec7b0bd1eaa Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 21 Dec 2020 20:58:34 +0100 Subject: [PATCH 035/146] meta: Update Crashpad to 2020-12-21 (#29) --- BUILD.gn | 13 - DEPS | 4 +- build/ios/setup_ios_gn.py | 1 - minidump/BUILD.gn | 2 - minidump/minidump_exception_writer_test.cc | 31 ++- minidump/minidump_handle_writer_test.cc | 65 +++-- minidump/minidump_memory_info_writer_test.cc | 31 ++- minidump/minidump_misc_info_writer_test.cc | 27 +- minidump/minidump_module_writer_test.cc | 95 ++++--- minidump/minidump_system_info_writer_test.cc | 23 +- minidump/minidump_thread_writer_test.cc | 48 ++-- .../test/minidump_memory_writer_test_util.cc | 18 +- snapshot/BUILD.gn | 4 +- snapshot/linux/debug_rendezvous.h | 2 +- snapshot/linux/debug_rendezvous_test.cc | 44 ++- snapshot/linux/process_reader_linux_test.cc | 223 +-------------- snapshot/linux/test_modules.cc | 262 ++++++++++++++++++ snapshot/linux/test_modules.h | 37 +++ .../minidump/process_snapshot_minidump.cc | 2 +- snapshot/snapshot_test.gyp | 2 + test/fuchsia_crashpad_tests.cmx | 3 + third_party/edo/BUILD.gn | 4 +- third_party/googletest/BUILD.gn | 1 - third_party/lss/README.crashpad | 2 +- third_party/mini_chromium/mini_chromium | 2 +- util/BUILD.gn | 3 +- util/file/file_io.cc | 14 +- util/linux/initial_signal_dispositions.cc | 3 +- util/linux/ptrace_broker.cc | 110 ++++---- util/linux/ptrace_broker.h | 13 +- util/linux/scoped_ptrace_attach.cc | 43 ++- util/linux/scoped_ptrace_attach.h | 18 ++ util/net/http_transport_socket.cc | 2 +- util/posix/scoped_mmap.cc | 71 ++++- util/posix/scoped_mmap.h | 6 +- util/stdlib/aligned_allocator.h | 2 +- 36 files changed, 746 insertions(+), 485 deletions(-) create mode 100644 snapshot/linux/test_modules.cc create mode 100644 snapshot/linux/test_modules.h diff --git a/BUILD.gn b/BUILD.gn index 7c0a794096..1ec9b97538 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -20,19 +20,6 @@ config("crashpad_config") { include_dirs = [ "." ] } -# TODO(fuchsia:46805): Remove this once instances of UB have been cleaned up. -config("disable_ubsan") { - if (crashpad_is_in_fuchsia) { - cflags = [ "-fno-sanitize=undefined" ] - } - visibility = [ - "snapshot:snapshot", - "minidump:minidump_test", - "third_party/googletest:googletest", - "util:util", - ] -} - if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { test("crashpad_tests") { deps = [ diff --git a/DEPS b/DEPS index fbff15550a..db8fd8d514 100644 --- a/DEPS +++ b/DEPS @@ -28,7 +28,7 @@ deps = { '9e121212d42be62a7cce38072f925f8398d11e49', 'crashpad/third_party/edo/edo': { 'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' + - '97121c64019fa0e8bfbc8254e3ccb5572c500746', + '6ffbf833173f53fcd06ecf08670a95cc01c01f72', 'condition': 'checkout_ios', }, 'crashpad/third_party/googletest/googletest': @@ -42,7 +42,7 @@ deps = { '7bde79cc274d06451bf65ae82c012a5d3e476b5a', 'crashpad/third_party/mini_chromium/mini_chromium': Var('chromium_git') + '/chromium/mini_chromium@' + - 'cb82d71291f19590d3ee138ba64fcf1e9e0edd84', + 'c748b289b825056985f3dd3b36dc86c766d787ad', 'crashpad/third_party/libfuzzer/src': Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' + 'fda403cf93ecb8792cb1d061564d89a6553ca020', diff --git a/build/ios/setup_ios_gn.py b/build/ios/setup_ios_gn.py index 5f1829ee20..9522ce6757 100755 --- a/build/ios/setup_ios_gn.py +++ b/build/ios/setup_ios_gn.py @@ -199,7 +199,6 @@ def GetGnCommand(self, gn_path, src_path, out_path, generate_xcode_project): gn_command = [gn_path, '--root=%s' % os.path.realpath(src_path), '-q'] if generate_xcode_project: gn_command.append('--ide=xcode') - gn_command.append('--root-target=gn_all') gn_command.append('--ninja-executable=autoninja') if self._settings.has_section('filters'): target_filters = self._settings.values('filters') diff --git a/minidump/BUILD.gn b/minidump/BUILD.gn index 6217edebd6..459fd6b751 100644 --- a/minidump/BUILD.gn +++ b/minidump/BUILD.gn @@ -186,6 +186,4 @@ source_set("minidump_test") { if (crashpad_is_win) { cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union } - - configs += [ "..:disable_ubsan" ] } diff --git a/minidump/minidump_exception_writer_test.cc b/minidump/minidump_exception_writer_test.cc index eed8f104b5..72837bba93 100644 --- a/minidump/minidump_exception_writer_test.cc +++ b/minidump/minidump_exception_writer_test.cc @@ -69,22 +69,27 @@ void ExpectExceptionStream(const MINIDUMP_EXCEPTION_STREAM* expected, const MinidumpContextX86** context) { EXPECT_EQ(observed->ThreadId, expected->ThreadId); EXPECT_EQ(observed->__alignment, 0u); - EXPECT_EQ(observed->ExceptionRecord.ExceptionCode, - expected->ExceptionRecord.ExceptionCode); - EXPECT_EQ(observed->ExceptionRecord.ExceptionFlags, - expected->ExceptionRecord.ExceptionFlags); - EXPECT_EQ(observed->ExceptionRecord.ExceptionRecord, - expected->ExceptionRecord.ExceptionRecord); - EXPECT_EQ(observed->ExceptionRecord.ExceptionAddress, - expected->ExceptionRecord.ExceptionAddress); - EXPECT_EQ(observed->ExceptionRecord.NumberParameters, - expected->ExceptionRecord.NumberParameters); + + // 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 < base::size(observed->ExceptionRecord.ExceptionInformation); + index < base::size(observed_exception.ExceptionInformation); ++index) { - EXPECT_EQ(observed->ExceptionRecord.ExceptionInformation[index], - expected->ExceptionRecord.ExceptionInformation[index]); + EXPECT_EQ(observed_exception.ExceptionInformation[index], + expected_exception.ExceptionInformation[index]); } *context = MinidumpWritableAtLocationDescriptor( file_contents, observed->ThreadContext); diff --git a/minidump/minidump_handle_writer_test.cc b/minidump/minidump_handle_writer_test.cc index 19a1036bd4..0776ae02c4 100644 --- a/minidump/minidump_handle_writer_test.cc +++ b/minidump/minidump_handle_writer_test.cc @@ -110,18 +110,17 @@ TEST(MinidumpHandleDataWriter, OneHandle) { GetHandleDataStream(string_file.string(), &handle_data_stream)); EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 1u); - const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor = - reinterpret_cast( - &handle_data_stream[1]); - EXPECT_EQ(handle_descriptor->Handle, handle_snapshot.handle); + 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)), + 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); + 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) { @@ -168,34 +167,34 @@ TEST(MinidumpHandleDataWriter, RepeatedTypeName) { GetHandleDataStream(string_file.string(), &handle_data_stream)); EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 2u); - const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor = - reinterpret_cast( - &handle_data_stream[1]); - EXPECT_EQ(handle_descriptor->Handle, handle_snapshot.handle); + 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)), + 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); - - const MINIDUMP_HANDLE_DESCRIPTOR* handle_descriptor2 = - reinterpret_cast( - reinterpret_cast(&handle_data_stream[1]) + - sizeof(MINIDUMP_HANDLE_DESCRIPTOR)); - EXPECT_EQ(handle_descriptor2->Handle, handle_snapshot2.handle); + 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)), + 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.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); + EXPECT_EQ(handle_descriptor2.TypeNameRva, handle_descriptor.TypeNameRva); } } // namespace diff --git a/minidump/minidump_memory_info_writer_test.cc b/minidump/minidump_memory_info_writer_test.cc index 634d3f1af1..e45ea57330 100644 --- a/minidump/minidump_memory_info_writer_test.cc +++ b/minidump/minidump_memory_info_writer_test.cc @@ -74,7 +74,11 @@ TEST(MinidumpMemoryInfoWriter, Empty) { ASSERT_NO_FATAL_FAILURE( GetMemoryInfoListStream(string_file.string(), &memory_info_list)); - EXPECT_EQ(memory_info_list->NumberOfEntries, 0u); + 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) { @@ -115,16 +119,21 @@ TEST(MinidumpMemoryInfoWriter, OneRegion) { ASSERT_NO_FATAL_FAILURE( GetMemoryInfoListStream(string_file.string(), &memory_info_list)); - EXPECT_EQ(memory_info_list->NumberOfEntries, 1u); - const MINIDUMP_MEMORY_INFO* memory_info = - reinterpret_cast(&memory_info_list[1]); - 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); + 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 diff --git a/minidump/minidump_misc_info_writer_test.cc b/minidump/minidump_misc_info_writer_test.cc index b7ea3d041a..7e93fc5f6d 100644 --- a/minidump/minidump_misc_info_writer_test.cc +++ b/minidump/minidump_misc_info_writer_test.cc @@ -171,20 +171,27 @@ void ExpectMiscInfoEqual( ExpectMiscInfoEqual( reinterpret_cast(expected), reinterpret_cast(observed)); - EXPECT_EQ(observed->XStateData.SizeOfInfo, expected->XStateData.SizeOfInfo); - EXPECT_EQ(observed->XStateData.ContextSize, expected->XStateData.ContextSize); - EXPECT_EQ(observed->XStateData.EnabledFeatures, - expected->XStateData.EnabledFeatures); + + 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 < base::size(observed->XStateData.Features); + feature_index < base::size(observed_misc_info.XStateData.Features); ++feature_index) { SCOPED_TRACE(base::StringPrintf("feature_index %" PRIuS, feature_index)); - EXPECT_EQ(observed->XStateData.Features[feature_index].Offset, - expected->XStateData.Features[feature_index].Offset); - EXPECT_EQ(observed->XStateData.Features[feature_index].Size, - expected->XStateData.Features[feature_index].Size); + 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->ProcessCookie, expected->ProcessCookie); + EXPECT_EQ(observed_misc_info.ProcessCookie, expected_misc_info.ProcessCookie); } TEST(MinidumpMiscInfoWriter, Empty) { diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc index 71db3ac171..e40d1af1a9 100644 --- a/minidump/minidump_module_writer_test.cc +++ b/minidump/minidump_module_writer_test.cc @@ -213,56 +213,68 @@ void ExpectModule(const MINIDUMP_MODULE* expected, const char* expected_debug_name, uint32_t expected_debug_type, bool expected_debug_utf16) { - 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, + 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->VersionInfo.dwStrucVersion, + EXPECT_EQ(observed_module.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); - EXPECT_EQ(observed->Reserved0, 0u); - EXPECT_EQ(observed->Reserved1, 0u); - - EXPECT_NE(observed->ModuleNameRva, 0u); + 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); base::string16 observed_module_name_utf16 = - MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva); + MinidumpStringAtRVAAsString(file_contents, observed_module.ModuleNameRva); base::string16 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->CvRecord, + 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->MiscRecord, - file_contents, - expected_debug_name, - expected_debug_type, - expected_debug_utf16)); + 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 @@ -300,8 +312,13 @@ void ExpectModuleWithBuildIDCv(const MINIDUMP_MODULE* expected, expected->VersionInfo.dwFileDateMS); EXPECT_EQ(observed->VersionInfo.dwFileDateLS, expected->VersionInfo.dwFileDateLS); - EXPECT_EQ(observed->Reserved0, 0u); - EXPECT_EQ(observed->Reserved1, 0u); + + 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); base::string16 observed_module_name_utf16 = diff --git a/minidump/minidump_system_info_writer_test.cc b/minidump/minidump_system_info_writer_test.cc index 99c599c14c..206a8e41cc 100644 --- a/minidump/minidump_system_info_writer_test.cc +++ b/minidump/minidump_system_info_writer_test.cc @@ -109,8 +109,11 @@ TEST(MinidumpSystemInfoWriter, Empty) { EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, 0u); EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, 0u); EXPECT_EQ(system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures, 0u); - EXPECT_EQ(system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0], 0u); - EXPECT_EQ(system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1], 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'); } @@ -234,10 +237,11 @@ TEST(MinidumpSystemInfoWriter, AMD64_Mac) { EXPECT_EQ(system_info->BuildNumber, kOSVersionBuild); EXPECT_EQ(system_info->PlatformId, kOS); EXPECT_EQ(system_info->SuiteMask, 0u); - EXPECT_EQ(system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0], - kCPUFeatures[0]); - EXPECT_EQ(system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1], - kCPUFeatures[1]); + + 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) { @@ -457,9 +461,12 @@ TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) { 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.OtherCpuInfo.ProcessorFeatures[0], + + 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(system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1], + EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1]); for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) { diff --git a/minidump/minidump_thread_writer_test.cc b/minidump/minidump_thread_writer_test.cc index e682b53d2d..3f3d56e830 100644 --- a/minidump/minidump_thread_writer_test.cc +++ b/minidump/minidump_thread_writer_test.cc @@ -108,33 +108,41 @@ void ExpectThread(const MINIDUMP_THREAD* expected, const std::string& file_contents, const MINIDUMP_MEMORY_DESCRIPTOR** stack, const void** context_base) { - EXPECT_EQ(observed->ThreadId, expected->ThreadId); - EXPECT_EQ(observed->SuspendCount, expected->SuspendCount); - EXPECT_EQ(observed->PriorityClass, expected->PriorityClass); - EXPECT_EQ(observed->Priority, expected->Priority); - EXPECT_EQ(observed->Teb, expected->Teb); - - EXPECT_EQ(observed->Stack.StartOfMemoryRange, - expected->Stack.StartOfMemoryRange); - EXPECT_EQ(observed->Stack.Memory.DataSize, expected->Stack.Memory.DataSize); + 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->Stack.Memory.DataSize, 0u); - ASSERT_NE(observed->Stack.Memory.Rva, 0u); + ASSERT_NE(observed_thread.Stack.Memory.DataSize, 0u); + ASSERT_NE(observed_thread.Stack.Memory.Rva, 0u); ASSERT_GE(file_contents.size(), - observed->Stack.Memory.Rva + observed->Stack.Memory.DataSize); + observed_thread.Stack.Memory.Rva + + observed_thread.Stack.Memory.DataSize); *stack = &observed->Stack; } else { - EXPECT_EQ(observed->Stack.StartOfMemoryRange, 0u); - EXPECT_EQ(observed->Stack.Memory.DataSize, 0u); - EXPECT_EQ(observed->Stack.Memory.Rva, 0u); + 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->ThreadContext.DataSize, expected->ThreadContext.DataSize); - ASSERT_NE(observed->ThreadContext.DataSize, 0u); - ASSERT_NE(observed->ThreadContext.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->ThreadContext.Rva + expected->ThreadContext.DataSize); - *context_base = &file_contents[observed->ThreadContext.Rva]; + observed_thread.ThreadContext.Rva + + expected_thread.ThreadContext.DataSize); + *context_base = &file_contents[observed_thread.ThreadContext.Rva]; } TEST(MinidumpThreadWriter, OneThread_x86_NoStack) { diff --git a/minidump/test/minidump_memory_writer_test_util.cc b/minidump/test/minidump_memory_writer_test_util.cc index f198abebd9..4e38cfa638 100644 --- a/minidump/test/minidump_memory_writer_test_util.cc +++ b/minidump/test/minidump_memory_writer_test_util.cc @@ -38,12 +38,20 @@ void TestMinidumpMemoryWriter::SetShouldFailRead(bool should_fail) { void ExpectMinidumpMemoryDescriptor( const MINIDUMP_MEMORY_DESCRIPTOR* expected, const MINIDUMP_MEMORY_DESCRIPTOR* observed) { - EXPECT_EQ(observed->StartOfMemoryRange, expected->StartOfMemoryRange); - EXPECT_EQ(observed->Memory.DataSize, expected->Memory.DataSize); - if (expected->Memory.Rva != 0) { + 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->Memory.Rva, - (expected->Memory.Rva + kMemoryAlignment - 1) & + EXPECT_EQ(observed_descriptor.Memory.Rva, + (expected_descriptor.Memory.Rva + kMemoryAlignment - 1) & ~(kMemoryAlignment - 1)); } } diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn index 6ca9bd40cb..82f8dd76fd 100644 --- a/snapshot/BUILD.gn +++ b/snapshot/BUILD.gn @@ -239,8 +239,6 @@ crashpad_static_library("snapshot") { public_configs = [ "..:crashpad_config" ] - configs = [ "..:disable_ubsan" ] - public_deps = [ ":context" ] deps = [ @@ -368,6 +366,8 @@ source_set("snapshot_test") { "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", ] diff --git a/snapshot/linux/debug_rendezvous.h b/snapshot/linux/debug_rendezvous.h index 0c30e1ab92..90106c9d18 100644 --- a/snapshot/linux/debug_rendezvous.h +++ b/snapshot/linux/debug_rendezvous.h @@ -41,7 +41,7 @@ class DebugRendezvous { //! \brief The difference between the preferred load address in the ELF file //! and the actual loaded address in memory. - LinuxVMOffset load_bias; + VMAddress load_bias; //! \brief The address of the dynamic array for this object. LinuxVMAddress dynamic_array; diff --git a/snapshot/linux/debug_rendezvous_test.cc b/snapshot/linux/debug_rendezvous_test.cc index 45ba65025f..d32bd19370 100644 --- a/snapshot/linux/debug_rendezvous_test.cc +++ b/snapshot/linux/debug_rendezvous_test.cc @@ -27,6 +27,7 @@ #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" @@ -34,6 +35,9 @@ #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" @@ -45,6 +49,20 @@ 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 @@ -112,9 +130,11 @@ void TestAgainstTarget(PtraceConnection* connection) { // Android's loader doesn't set the load bias until Android 4.3 (API 18). if (android_runtime_api >= 18) { - EXPECT_EQ(debug.Executable()->load_bias, exe_reader.GetLoadBias()); + ExpectLoadBias(connection->Is64Bit(), + debug.Executable()->load_bias, + exe_reader.GetLoadBias()); } else { - EXPECT_EQ(debug.Executable()->load_bias, 0); + EXPECT_EQ(debug.Executable()->load_bias, 0u); } for (const DebugRendezvous::LinkEntry& module : debug.Modules()) { @@ -130,7 +150,7 @@ void TestAgainstTarget(PtraceConnection* connection) { // (API 17). if (is_android_loader && android_runtime_api < 17) { EXPECT_EQ(module.dynamic_array, 0u); - EXPECT_EQ(module.load_bias, 0); + EXPECT_EQ(module.load_bias, 0u); continue; } @@ -170,7 +190,11 @@ void TestAgainstTarget(PtraceConnection* connection) { 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); @@ -185,9 +209,11 @@ void TestAgainstTarget(PtraceConnection* connection) { // (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, 0); + EXPECT_EQ(module.load_bias, 0u); } else { - EXPECT_EQ(module.load_bias, module_reader->GetLoadBias()); + ExpectLoadBias(connection->Is64Bit(), + module.load_bias, + static_cast(module_reader->GetLoadBias())); } CheckedLinuxAddressRange module_range( @@ -197,6 +223,14 @@ void TestAgainstTarget(PtraceConnection* connection) { } 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())); diff --git a/snapshot/linux/process_reader_linux_test.cc b/snapshot/linux/process_reader_linux_test.cc index 98b99e6595..6373a3a01f 100644 --- a/snapshot/linux/process_reader_linux_test.cc +++ b/snapshot/linux/process_reader_linux_test.cc @@ -37,6 +37,7 @@ #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" @@ -541,218 +542,7 @@ void ExpectModulesFromSelf( #endif // !OS_ANDROID || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 } -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); - - constexpr size_t load2_vaddr = 0x200000; - - module.phdr_table.load1.p_type = PT_LOAD; - module.phdr_table.load1.p_offset = 0; - module.phdr_table.load1.p_vaddr = 0; - module.phdr_table.load1.p_filesz = offsetof(decltype(module), shdr_table); - module.phdr_table.load1.p_memsz = offsetof(decltype(module), shdr_table); - module.phdr_table.load1.p_flags = PF_R; - module.phdr_table.load1.p_align = load2_vaddr; - - 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 = offsetof(decltype(module), shdr_table); - module.phdr_table.load2.p_memsz = offsetof(decltype(module), shdr_table); - module.phdr_table.load2.p_flags = PF_R | PF_W; - module.phdr_table.load2.p_align = load2_vaddr; - - 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 = offsetof(decltype(module), hash_table); - module.dynamic_array.strtab.d_tag = DT_STRTAB; - module.dynamic_array.strtab.d_un.d_ptr = - offsetof(decltype(module), string_table); - module.dynamic_array.symtab.d_tag = DT_SYMTAB; - module.dynamic_array.symtab.d_un.d_ptr = - 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; - - CHECK_GE(sizeof(module.string_table), soname.size() + 2); - 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(); - 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; -} - +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) void ExpectTestModule(ProcessReaderLinux* reader, const std::string& module_name) { for (const auto& module : reader->Modules()) { @@ -771,13 +561,16 @@ void ExpectTestModule(ProcessReaderLinux* reader, } 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()); @@ -786,7 +579,9 @@ TEST(ProcessReaderLinux, SelfModules) { 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 { @@ -806,13 +601,17 @@ class ChildModuleTest : public Multiprocess { 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))); diff --git a/snapshot/linux/test_modules.cc b/snapshot/linux/test_modules.cc new file mode 100644 index 0000000000..b2450c2067 --- /dev/null +++ b/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/snapshot/linux/test_modules.h b/snapshot/linux/test_modules.h new file mode 100644 index 0000000000..b791480a1d --- /dev/null +++ b/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/snapshot/minidump/process_snapshot_minidump.cc b/snapshot/minidump/process_snapshot_minidump.cc index c42e7ccfa0..4f85b77cfd 100644 --- a/snapshot/minidump/process_snapshot_minidump.cc +++ b/snapshot/minidump/process_snapshot_minidump.cc @@ -318,7 +318,7 @@ bool ProcessSnapshotMinidump::InitializeMiscInfo() { #else full_version_ = base::UTF16ToUTF8(info.BuildString); #endif - full_version_ = full_version_.substr(0, full_version_.find(";")); + full_version_ = full_version_.substr(0, full_version_.find(';')); FALLTHROUGH; case sizeof(MINIDUMP_MISC_INFO_3): case sizeof(MINIDUMP_MISC_INFO_2): diff --git a/snapshot/snapshot_test.gyp b/snapshot/snapshot_test.gyp index 43de7254a3..a4d4d9e00f 100644 --- a/snapshot/snapshot_test.gyp +++ b/snapshot/snapshot_test.gyp @@ -80,6 +80,8 @@ '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', 'mac/cpu_context_mac_test.cc', 'mac/mach_o_image_annotations_reader_test.cc', 'mac/mach_o_image_reader_test.cc', diff --git a/test/fuchsia_crashpad_tests.cmx b/test/fuchsia_crashpad_tests.cmx index b25208d88b..87004b2901 100644 --- a/test/fuchsia_crashpad_tests.cmx +++ b/test/fuchsia_crashpad_tests.cmx @@ -7,6 +7,9 @@ } } }, + "include": [ + "sdk/lib/diagnostics/syslog/client.shard.cmx" + ], "program": { "binary": "test/crashpad_tests" }, diff --git a/third_party/edo/BUILD.gn b/third_party/edo/BUILD.gn index 2b8d5b994f..73dca1aab4 100644 --- a/third_party/edo/BUILD.gn +++ b/third_party/edo/BUILD.gn @@ -124,8 +124,8 @@ if (crashpad_is_in_chromium) { "edo/Service/Sources/NSKeyedArchiver+EDOAdditions.m", "edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.h", "edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.m", - "edo/Service/Sources/NSObject+EDOBlacklistedType.h", - "edo/Service/Sources/NSObject+EDOBlacklistedType.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", diff --git a/third_party/googletest/BUILD.gn b/third_party/googletest/BUILD.gn index 4ad44a43e3..f51db0e9d5 100644 --- a/third_party/googletest/BUILD.gn +++ b/third_party/googletest/BUILD.gn @@ -28,7 +28,6 @@ if (crashpad_is_in_chromium) { group("googletest") { testonly = true public_deps = [ "//third_party/googletest:gtest" ] - public_configs = [ "../..:disable_ubsan" ] } group("googlemock") { testonly = true diff --git a/third_party/lss/README.crashpad b/third_party/lss/README.crashpad index c036ae0f22..d1ac9913ed 100644 --- a/third_party/lss/README.crashpad +++ b/third_party/lss/README.crashpad @@ -3,7 +3,7 @@ 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 +License File: lss/linux_syscall_support.h Security Critical: yes Description: diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index cb82d71291..c748b289b8 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit cb82d71291f19590d3ee138ba64fcf1e9e0edd84 +Subproject commit c748b289b825056985f3dd3b36dc86c766d787ad diff --git a/util/BUILD.gn b/util/BUILD.gn index ab0d3294be..adedf9545b 100644 --- a/util/BUILD.gn +++ b/util/BUILD.gn @@ -77,6 +77,7 @@ if (crashpad_is_mac || crashpad_is_ios) { migcom_path, ] } + deps = [ "//build/config/mac:sdk_inputs" ] } if (sysroot != "") { if (crashpad_is_in_chromium) { @@ -564,8 +565,6 @@ crashpad_static_library("util") { public_configs = [ "..:crashpad_config" ] - configs = [ "..:disable_ubsan" ] - # Include generated files starting with "util". if (crashpad_is_in_fuchsia) { include_dirs = [ "$root_gen_dir/third_party/crashpad" ] diff --git a/util/file/file_io.cc b/util/file/file_io.cc index c1da67226e..3bd86cc465 100644 --- a/util/file/file_io.cc +++ b/util/file/file_io.cc @@ -66,11 +66,12 @@ class FileIOWriteAll final : public internal::WriteAllInternal { namespace internal { bool ReadExactlyInternal::ReadExactly(void* buffer, size_t size, bool can_log) { - char* buffer_c = static_cast(buffer); + uintptr_t buffer_int = reinterpret_cast(buffer); size_t total_bytes = 0; size_t remaining = size; while (remaining > 0) { - FileOperationResult bytes_read = Read(buffer_c, remaining, can_log); + FileOperationResult bytes_read = + Read(reinterpret_cast(buffer_int), remaining, can_log); if (bytes_read < 0) { return false; } @@ -81,7 +82,7 @@ bool ReadExactlyInternal::ReadExactly(void* buffer, size_t size, bool can_log) { break; } - buffer_c += bytes_read; + buffer_int += bytes_read; remaining -= bytes_read; total_bytes += bytes_read; } @@ -96,17 +97,18 @@ bool ReadExactlyInternal::ReadExactly(void* buffer, size_t size, bool can_log) { } bool WriteAllInternal::WriteAll(const void* buffer, size_t size) { - const char* buffer_c = static_cast(buffer); + uintptr_t buffer_int = reinterpret_cast(buffer); while (size > 0) { - FileOperationResult bytes_written = Write(buffer_c, size); + FileOperationResult bytes_written = + Write(reinterpret_cast(buffer_int), size); if (bytes_written < 0) { return false; } DCHECK_NE(bytes_written, 0); - buffer_c += bytes_written; + buffer_int += bytes_written; size -= bytes_written; } diff --git a/util/linux/initial_signal_dispositions.cc b/util/linux/initial_signal_dispositions.cc index b72b2476ed..9d980a11fb 100644 --- a/util/linux/initial_signal_dispositions.cc +++ b/util/linux/initial_signal_dispositions.cc @@ -20,9 +20,10 @@ #include "base/logging.h" #include "build/build_config.h" +namespace crashpad { + #if __ANDROID_API__ <= 23 -namespace crashpad { namespace { bool LoggingSignal(int signum, sighandler_t handler) { sighandler_t previous = signal(signum, handler); diff --git a/util/linux/ptrace_broker.cc b/util/linux/ptrace_broker.cc index b6b5bb131d..68621f2cc5 100644 --- a/util/linux/ptrace_broker.cc +++ b/util/linux/ptrace_broker.cc @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -24,7 +25,11 @@ #include "base/check_op.h" #include "base/posix/eintr_wrapper.h" +#include "base/process/process_metrics.h" +#include "third_party/lss/lss.h" +#include "util/linux/scoped_ptrace_attach.h" #include "util/misc/memory_sanitizer.h" +#include "util/posix/scoped_mmap.h" namespace crashpad { @@ -52,18 +57,58 @@ size_t FormatPID(char* buffer, pid_t pid) { } // namespace +class PtraceBroker::AttachmentsArray { + public: + AttachmentsArray() : allocation_(false), attach_count_(0) {} + + ~AttachmentsArray() { + for (size_t index = 0; index < attach_count_; ++index) { + PtraceDetach(Attachments()[index], false); + } + } + + bool Initialize() { + return allocation_.ResetMmap(nullptr, + base::GetPageSize(), + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0); + } + + bool Attach(pid_t pid) { + pid_t* attach = AllocateAttachment(); + if (!attach || !PtraceAttach(pid, false)) { + return false; + } + + *attach = pid; + return true; + } + + private: + pid_t* AllocateAttachment() { + if (attach_count_ >= (allocation_.len() / sizeof(pid_t))) { + return nullptr; + } + return &Attachments()[attach_count_++]; + } + + pid_t* Attachments() { return allocation_.addr_as(); } + + ScopedMmap allocation_; + size_t attach_count_; + + DISALLOW_COPY_AND_ASSIGN(AttachmentsArray); +}; + PtraceBroker::PtraceBroker(int sock, pid_t pid, bool is_64_bit) : ptracer_(is_64_bit, /* can_log= */ false), file_root_(file_root_buffer_), - attachments_(nullptr), - attach_count_(0), - attach_capacity_(0), memory_file_(), sock_(sock), memory_pid_(pid), tried_opening_mem_file_(false) { - AllocateAttachments(); - static constexpr char kProc[] = "/proc/"; size_t root_length = strlen(kProc); memcpy(file_root_buffer_, kProc, root_length); @@ -88,35 +133,12 @@ void PtraceBroker::SetFileRoot(const char* new_root) { } int PtraceBroker::Run() { - int result = RunImpl(); - ReleaseAttachments(); - return result; -} - -bool PtraceBroker::AllocateAttachments() { - constexpr size_t page_size = 4096; - constexpr size_t alloc_size = - (sizeof(ScopedPtraceAttach) + page_size - 1) & ~(page_size - 1); - void* alloc = sbrk(alloc_size); - if (reinterpret_cast(alloc) == -1) { - return false; - } - - if (attachments_ == nullptr) { - attachments_ = reinterpret_cast(alloc); - } - - attach_capacity_ += alloc_size / sizeof(ScopedPtraceAttach); - return true; + AttachmentsArray attachments; + attachments.Initialize(); + return RunImpl(&attachments); } -void PtraceBroker::ReleaseAttachments() { - for (size_t index = 0; index < attach_count_; ++index) { - attachments_[index].Reset(); - } -} - -int PtraceBroker::RunImpl() { +int PtraceBroker::RunImpl(AttachmentsArray* attachments) { while (true) { Request request = {}; if (!ReadFileExactly(sock_, &request, sizeof(request))) { @@ -129,25 +151,10 @@ int PtraceBroker::RunImpl() { switch (request.type) { case Request::kTypeAttach: { - ScopedPtraceAttach* attach; - ScopedPtraceAttach stack_attach; - bool attach_on_stack = false; - - if (attach_capacity_ > attach_count_ || AllocateAttachments()) { - attach = new (&attachments_[attach_count_]) ScopedPtraceAttach; - } else { - attach = &stack_attach; - attach_on_stack = true; - } - ExceptionHandlerProtocol::Bool status = - ExceptionHandlerProtocol::kBoolFalse; - if (attach->ResetAttach(request.tid)) { - status = ExceptionHandlerProtocol::kBoolTrue; - if (!attach_on_stack) { - ++attach_count_; - } - } + attachments->Attach(request.tid) + ? ExceptionHandlerProtocol::kBoolTrue + : ExceptionHandlerProtocol::kBoolFalse; if (!WriteFile(sock_, &status, sizeof(status))) { return errno; @@ -160,9 +167,6 @@ int PtraceBroker::RunImpl() { } } - if (attach_on_stack && status == ExceptionHandlerProtocol::kBoolTrue) { - return RunImpl(); - } continue; } diff --git a/util/linux/ptrace_broker.h b/util/linux/ptrace_broker.h index 6a7bfb723d..5d90cb2027 100644 --- a/util/linux/ptrace_broker.h +++ b/util/linux/ptrace_broker.h @@ -24,7 +24,6 @@ #include "util/linux/exception_handler_protocol.h" #include "util/linux/ptrace_connection.h" #include "util/linux/ptracer.h" -#include "util/linux/scoped_ptrace_attach.h" #include "util/linux/thread_info.h" #include "util/misc/address_types.h" @@ -186,16 +185,13 @@ class PtraceBroker { //! This method returns when a PtraceBrokerRequest with type kTypeExit is //! received or an error is encountered on the socket. //! - //! This method calls `sbrk`, which may break other memory management tools, - //! such as `malloc`. - //! //! \return 0 if Run() exited due to an exit request. Otherwise an error code. int Run(); private: - bool AllocateAttachments(); - void ReleaseAttachments(); - int RunImpl(); + class AttachmentsArray; + + int RunImpl(AttachmentsArray*); int SendError(ExceptionHandlerProtocol::Errno err); int SendReadError(ReadError err); int SendOpenResult(OpenResult result); @@ -210,9 +206,6 @@ class PtraceBroker { char file_root_buffer_[32]; Ptracer ptracer_; const char* file_root_; - ScopedPtraceAttach* attachments_; - size_t attach_count_; - size_t attach_capacity_; ScopedFileHandle memory_file_; int sock_; pid_t memory_pid_; diff --git a/util/linux/scoped_ptrace_attach.cc b/util/linux/scoped_ptrace_attach.cc index 09e3fba56c..eed8345563 100644 --- a/util/linux/scoped_ptrace_attach.cc +++ b/util/linux/scoped_ptrace_attach.cc @@ -22,6 +22,32 @@ namespace crashpad { +bool PtraceAttach(pid_t pid, bool can_log) { + if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) != 0) { + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } + + int status; + if (HANDLE_EINTR(waitpid(pid, &status, __WALL)) < 0) { + PLOG_IF(ERROR, can_log) << "waitpid"; + return false; + } + if (!WIFSTOPPED(status)) { + LOG_IF(ERROR, can_log) << "process not stopped"; + return false; + } + return true; +} + +bool PtraceDetach(pid_t pid, bool can_log) { + if (pid >= 0 && ptrace(PTRACE_DETACH, pid, nullptr, nullptr) != 0) { + PLOG_IF(ERROR, can_log) << "ptrace"; + return false; + } + return true; +} + ScopedPtraceAttach::ScopedPtraceAttach() : pid_(-1) {} @@ -30,8 +56,7 @@ ScopedPtraceAttach::~ScopedPtraceAttach() { } bool ScopedPtraceAttach::Reset() { - if (pid_ >= 0 && ptrace(PTRACE_DETACH, pid_, nullptr, nullptr) != 0) { - PLOG(ERROR) << "ptrace"; + if (!PtraceDetach(pid_, true)) { return false; } pid_ = -1; @@ -41,21 +66,11 @@ bool ScopedPtraceAttach::Reset() { bool ScopedPtraceAttach::ResetAttach(pid_t pid) { Reset(); - if (ptrace(PTRACE_ATTACH, pid, nullptr, nullptr) != 0) { - PLOG(ERROR) << "ptrace"; + if (!PtraceAttach(pid, true)) { return false; } - pid_ = pid; - int status; - if (HANDLE_EINTR(waitpid(pid_, &status, __WALL)) < 0) { - PLOG(ERROR) << "waitpid"; - return false; - } - if (!WIFSTOPPED(status)) { - LOG(ERROR) << "process not stopped"; - return false; - } + pid_ = pid; return true; } diff --git a/util/linux/scoped_ptrace_attach.h b/util/linux/scoped_ptrace_attach.h index a3d9d6987f..f380d254db 100644 --- a/util/linux/scoped_ptrace_attach.h +++ b/util/linux/scoped_ptrace_attach.h @@ -21,6 +21,24 @@ namespace crashpad { +//! \brief Attaches to the process with process ID \a pid and blocks until the +//! target process has stopped by calling `waitpid()`. +//! +//! \param pid The process ID of the process to attach to. +//! \param can_log Whether this function may log messages on failure. +//! \return `true` on success. `false` on failure with a message logged if \a +//! can_log is `true`. +bool PtraceAttach(pid_t pid, bool can_log = true); + +//! \brief Detaches the process with process ID \a pid. The process must +//! already be ptrace attached. +//! +//! \param pid The process ID of the process to detach. +//! \param can_log Whether this function may log messages on failure. +//! \return `true` on success. `false` on failure with a message logged if \a +//! ca_log is `true `true` +bool PtraceDetach(pid_t pid, bool can_log = true); + //! \brief Maintains a `ptrace()` attachment to a process. //! //! On destruction, the process will be detached. diff --git a/util/net/http_transport_socket.cc b/util/net/http_transport_socket.cc index ce548f3304..1d5ca878b1 100644 --- a/util/net/http_transport_socket.cc +++ b/util/net/http_transport_socket.cc @@ -419,7 +419,7 @@ bool WriteRequest(Stream* stream, } } - write_start = buf.crlf - size_len; + write_start = static_cast(buf.crlf) - size_len; write_size = size_len + sizeof(buf.crlf) + data_bytes + kCRLFSize; } else { // When not using chunked encoding, only use buf.data. diff --git a/util/posix/scoped_mmap.cc b/util/posix/scoped_mmap.cc index 0c98ba24a2..21533d2374 100644 --- a/util/posix/scoped_mmap.cc +++ b/util/posix/scoped_mmap.cc @@ -22,12 +22,54 @@ #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/numerics/safe_math.h" +#include "base/process/process_metrics.h" +#include "build/build_config.h" + +#if defined(OS_LINUX) +#include "third_party/lss/lss.h" +#endif namespace { -bool Munmap(uintptr_t addr, size_t len) { - if (munmap(reinterpret_cast(addr), len) != 0) { - PLOG(ERROR) << "munmap"; +#if defined(OS_LINUX) +void* CallMmap(void* addr, + size_t len, + int prot, + int flags, + int fd, + off_t offset) { + return sys_mmap(addr, len, prot, flags, fd, offset); +} + +int CallMunmap(void* addr, size_t len) { + return sys_munmap(addr, len); +} + +int CallMprotect(void* addr, size_t len, int prot) { + return sys_mprotect(addr, len, prot); +} +#else +void* CallMmap(void* addr, + size_t len, + int prot, + int flags, + int fd, + off_t offset) { + return mmap(addr, len, prot, flags, fd, offset); +} + +int CallMunmap(void* addr, size_t len) { + return munmap(addr, len); +} + +int CallMprotect(void* addr, size_t len, int prot) { + return mprotect(addr, len, prot); +} +#endif + +bool LoggingMunmap(uintptr_t addr, size_t len, bool can_log) { + if (CallMunmap(reinterpret_cast(addr), len) != 0) { + PLOG_IF(ERROR, can_log) << "munmap"; return false; } @@ -35,7 +77,7 @@ bool Munmap(uintptr_t addr, size_t len) { } size_t RoundPage(size_t size) { - const size_t kPageMask = base::checked_cast(getpagesize()) - 1; + const size_t kPageMask = base::checked_cast(base::GetPageSize()) - 1; return (size + kPageMask) & ~kPageMask; } @@ -43,11 +85,12 @@ size_t RoundPage(size_t size) { namespace crashpad { -ScopedMmap::ScopedMmap() {} +ScopedMmap::ScopedMmap(bool can_log) : can_log_(can_log) {} ScopedMmap::~ScopedMmap() { if (is_valid()) { - Munmap(reinterpret_cast(addr_), RoundPage(len_)); + LoggingMunmap( + reinterpret_cast(addr_), RoundPage(len_), can_log_); } } @@ -63,7 +106,7 @@ bool ScopedMmap::ResetAddrLen(void* addr, size_t len) { DCHECK_EQ(len, 0u); } else { DCHECK_NE(len, 0u); - DCHECK_EQ(new_addr % getpagesize(), 0u); + DCHECK_EQ(new_addr % base::GetPageSize(), 0u); DCHECK((base::CheckedNumeric(new_addr) + (new_len_round - 1)) .IsValid()); } @@ -74,11 +117,13 @@ bool ScopedMmap::ResetAddrLen(void* addr, size_t len) { const uintptr_t old_addr = reinterpret_cast(addr_); const size_t old_len_round = RoundPage(len_); if (old_addr < new_addr) { - result &= Munmap(old_addr, std::min(old_len_round, new_addr - old_addr)); + result &= LoggingMunmap( + old_addr, std::min(old_len_round, new_addr - old_addr), can_log_); } if (old_addr + old_len_round > new_addr + new_len_round) { uintptr_t unmap_start = std::max(old_addr, new_addr + new_len_round); - result &= Munmap(unmap_start, old_addr + old_len_round - unmap_start); + result &= LoggingMunmap( + unmap_start, old_addr + old_len_round - unmap_start, can_log_); } } @@ -100,9 +145,9 @@ bool ScopedMmap::ResetMmap(void* addr, // consider the return value from Reset(). Reset(); - void* new_addr = mmap(addr, len, prot, flags, fd, offset); + void* new_addr = CallMmap(addr, len, prot, flags, fd, offset); if (new_addr == MAP_FAILED) { - PLOG(ERROR) << "mmap"; + PLOG_IF(ERROR, can_log_) << "mmap"; return false; } @@ -113,8 +158,8 @@ bool ScopedMmap::ResetMmap(void* addr, } bool ScopedMmap::Mprotect(int prot) { - if (mprotect(addr_, RoundPage(len_), prot) < 0) { - PLOG(ERROR) << "mprotect"; + if (CallMprotect(addr_, RoundPage(len_), prot) < 0) { + PLOG_IF(ERROR, can_log_) << "mprotect"; return false; } diff --git a/util/posix/scoped_mmap.h b/util/posix/scoped_mmap.h index b497d944ff..12f5ceed71 100644 --- a/util/posix/scoped_mmap.h +++ b/util/posix/scoped_mmap.h @@ -30,7 +30,10 @@ namespace crashpad { //! will be released by calling `munmap()`. class ScopedMmap { public: - ScopedMmap(); + //! \brief Constructs this object. + //! + //! \param can_log `true` if methods of this class may log messages. + explicit ScopedMmap(bool can_log = true); ~ScopedMmap(); //! \brief Releases the memory-mapped region by calling `munmap()`. @@ -105,6 +108,7 @@ class ScopedMmap { private: void* addr_ = MAP_FAILED; size_t len_ = 0; + bool can_log_; DISALLOW_COPY_AND_ASSIGN(ScopedMmap); }; diff --git a/util/stdlib/aligned_allocator.h b/util/stdlib/aligned_allocator.h index 97be27a271..4e64744e69 100644 --- a/util/stdlib/aligned_allocator.h +++ b/util/stdlib/aligned_allocator.h @@ -69,7 +69,7 @@ struct AlignedAllocator { pointer address(reference x) const noexcept { return &x; } const_pointer address(const_reference x) const noexcept { return &x; } - pointer allocate(size_type n, std::allocator::const_pointer hint = 0) { + pointer allocate(size_type n, const void* hint = 0) { return reinterpret_cast( AlignedAllocate(Alignment, sizeof(value_type) * n)); } From ce53f69736efa9af3cd47baab3ac752509d0cecb Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 23 Dec 2020 14:25:36 +0100 Subject: [PATCH 036/146] fix: Empty CMake elseif --- util/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 7a3425f8da..e1ae91ad33 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -200,7 +200,7 @@ if(APPLE) process/process_memory_mac.cc process/process_memory_mac.h ) - elseif() + else() target_sources(crashpad_util PRIVATE ios/exception_processor.h ios/exception_processor.mm From 3fc9e71661c1b2388e4d33aa6eb19948bbb383e2 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 23 Dec 2020 14:50:17 +0100 Subject: [PATCH 037/146] fix: Link to UIKit framework on iOS --- util/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index e1ae91ad33..63b296f5d6 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -416,6 +416,11 @@ if(APPLE) "-framework Foundation" "-framework IOKit" ) + if(IOS) + target_link_libraries(crashpad_util PRIVATE + "-framework UIKit" + ) + endif() endif() if(LINUX) From a26ad17363f7f292b6ed1c382717b0d1928d4555 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 4 Jan 2021 13:00:42 +0100 Subject: [PATCH 038/146] cmake: Fix cross build from Mac x64 to Mac arm64 (#31) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Romain Roffé --- util/CMakeLists.txt | 24 +++++++++++++++++++++++- util/mach/mig_gen.py | 7 ++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 63b296f5d6..ff9ae0c7bd 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -375,6 +375,22 @@ if(APPLE) endif() list(TRANSFORM includes PREPEND "--include=") + if(CMAKE_OSX_SYSROOT) + set(sdk --sdk ${CMAKE_OSX_SYSROOT}) + endif() + + # When building for Xcode, the `CMAKE_OSX_SYSROOT` is not set to a proper + # directory, but rather is `iphoneos`, which confuses `mig`. + # Also, Xcode uses a different `SDKROOT` depending on the `-sdk` flag + # provided to `xcodebuild`. + # Similarly, we don't know the arch at configure-time, because it changes + # at build time depending on the `-sdk` flag as well. + # We hack around this by consuming the arch list from the env. + if(XCODE) + set(archs --arch "FROM_ENV") + set(sdk --sdk "$SDKROOT") + endif() + # Create generate rule for each input file. Add each generated output # as a source to the target. foreach(input ${input_files}) @@ -387,7 +403,7 @@ if(APPLE) OUTPUT ${output_files} COMMAND - "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" ${archs} ${includes} "${input}" ${output_files} + "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" ${archs} ${sdk} ${includes} "${input}" ${output_files} DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" ) @@ -410,6 +426,12 @@ target_link_libraries(crashpad_util ) if(APPLE) + get_property(archs TARGET crashpad_util PROPERTY OSX_ARCHITECTURES) + if (archs) + list(TRANSFORM archs PREPEND "-arch ") + set(CMAKE_ASM_FLAGS "${CFLAGS} ${archs}") + endif() + target_link_libraries(crashpad_util PRIVATE bsm "-framework CoreFoundation" diff --git a/util/mach/mig_gen.py b/util/mach/mig_gen.py index 99b4f7ec6b..b3ef614e7a 100755 --- a/util/mach/mig_gen.py +++ b/util/mach/mig_gen.py @@ -83,7 +83,12 @@ def parse_args(args, multiple_arch=False): parser.add_argument('server_c') parser.add_argument('user_h') parser.add_argument('server_h') - return parser.parse_args(args) + + # This is a HACK to parse arch from env when cmake is configured to use xcode + parsed = parser.parse_args(args) + if multiple_arch and len(parsed.arch) == 1 and parsed.arch[0] == "FROM_ENV": + parsed.arch = os.environ.get("ARCHS", "").split(" ") + return parsed def main(args): From aeb8be5238ac48123ca89bb8a5ccaf3560f5bfc2 Mon Sep 17 00:00:00 2001 From: Siim Meerits Date: Mon, 11 Jan 2021 00:11:27 +0200 Subject: [PATCH 039/146] cmake: Install 'handler' and 'util' development header files. (#32) --- handler/CMakeLists.txt | 4 ++++ util/CMakeLists.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 3288ac46b2..073aff2093 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -74,6 +74,10 @@ 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 diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index ff9ae0c7bd..cb25aad393 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -487,3 +487,7 @@ set_property(TARGET crashpad_util PROPERTY EXPORT_NAME util) add_library(crashpad::util ALIAS crashpad_util) crashpad_install_target(crashpad_util) +crashpad_install_dev(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crashpad/util" + FILES_MATCHING PATTERN "*.h" +) From 3c9de32f2bf7878c10a47e5957d02b05465f25af Mon Sep 17 00:00:00 2001 From: daxpedda Date: Wed, 20 Jan 2021 12:47:47 +0100 Subject: [PATCH 040/146] fix: Add missing flags for zlib compilation (#22) --- third_party/zlib/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 451433171f..2f9a87dedd 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -48,6 +48,10 @@ else() zlib/x86.c zlib/x86.h ) + + if(NOT MSVC) + target_compile_options(crashpad_zlib PRIVATE -msse4.2 -mpclmul) + endif() endif() target_compile_definitions(crashpad_zlib PUBLIC CRASHPAD_ZLIB_SOURCE_EMBEDDED From 358403ca327392337e77f3cc0a27766e3639e2d5 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 25 Jan 2021 11:39:50 +0100 Subject: [PATCH 041/146] update submodule and sync build files --- compat/CMakeLists.txt | 13 ------------- third_party/mini_chromium/mini_chromium | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/compat/CMakeLists.txt b/compat/CMakeLists.txt index 50be8a456e..e4f430360c 100644 --- a/compat/CMakeLists.txt +++ b/compat/CMakeLists.txt @@ -12,10 +12,7 @@ if(APPLE) ) else() list(APPEND COMPAT_SOURCES - non_mac/mach-o/loader.h non_mac/mach/mach.h - non_mac/mach/machine.h - non_mac/mach/vm_prot.h ) endif() @@ -85,12 +82,6 @@ else() ) endif() -if(NOT LINUX AND NOT ANDROID) - list(APPEND COMPAT_SOURCES - non_elf/elf.h - ) -endif() - if(APPLE) add_library(crashpad_compat INTERFACE) set(TI_TYPE "INTERFACE") @@ -117,8 +108,6 @@ endif() if(APPLE) target_include_directories(crashpad_compat ${TI_TYPE} "$") -else() - target_include_directories(crashpad_compat ${TI_TYPE} "$") endif() if(IOS) @@ -127,8 +116,6 @@ endif() if(LINUX OR ANDROID) target_include_directories(crashpad_compat ${TI_TYPE} "$") -else() - target_include_directories(crashpad_compat ${TI_TYPE} "$") endif() if(ANDROID) diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index c748b289b8..12ea507eb7 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit c748b289b825056985f3dd3b36dc86c766d787ad +Subproject commit 12ea507eb719a54698e1429e91e84c65284805ab From ee06cb2b2d0f99bd432f00117ee3a676fc55d11b Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 25 Jan 2021 12:00:16 +0100 Subject: [PATCH 042/146] hardcode chromeos_buildflags --- third_party/mini_chromium/CMakeLists.txt | 4 ++++ .../mini_chromium/build/chromeos_buildflags.h | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 third_party/mini_chromium/build/chromeos_buildflags.h diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index cece468fc9..907ddc3bbf 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -4,6 +4,7 @@ function(mc_append_sources) 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 @@ -172,6 +173,9 @@ target_include_directories(mini_chromium PUBLIC "$" $ ) +target_include_directories(mini_chromium PUBLIC + "$" +) target_link_libraries(mini_chromium PRIVATE $ diff --git a/third_party/mini_chromium/build/chromeos_buildflags.h b/third_party/mini_chromium/build/chromeos_buildflags.h new file mode 100644 index 0000000000..c54f076845 --- /dev/null +++ b/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 BUILDFLAG_INTERNAL_IS_CHROMEOS_LACROS() (0) +#define BUILDFLAG_INTERNAL_IS_CHROMEOS_ASH() (0) + +#endif // MINI_CHROMIUM_BUILD_CHROMEOS_BUILDFLAGS_H_ From cb520bd41fdea18a4d842efabeac61a2a5beaa61 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 26 Jan 2021 12:13:39 +0100 Subject: [PATCH 043/146] remove conflicting gitignore of lss --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1e9f1ec064..64507fc480 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,6 @@ /third_party/linux/.cipd /third_party/linux/clang /third_party/linux/sysroot -/third_party/lss/lss /third_party/gyp/gyp /xcodebuild tags From e860f7f132163c0b52f405656ac1c8607a7d79ea Mon Sep 17 00:00:00 2001 From: daxpedda Date: Mon, 1 Mar 2021 10:23:00 +0100 Subject: [PATCH 044/146] fix: Fix cross-compilation from Intel to Apple Silicon MacOS. (#34) --- third_party/zlib/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 2f9a87dedd..f912af5ed6 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -41,7 +41,13 @@ else() zlib/zutil.h zlib_crashpad.h ) - if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(x86)|(i[3-7]86)|(AMD64)") + if (APPLE) + get_property(archs TARGET crashpad_zlib PROPERTY OSX_ARCHITECTURES) + endif() + if(NOT archs) + set(archs ${CMAKE_SYSTEM_PROCESSOR}) + endif() + if(archs MATCHES "(x86_64)|(x86)|(i[3-7]86)|(AMD64)") target_sources(crashpad_zlib PRIVATE zlib/crc_folding.c zlib/fill_window_sse.c @@ -52,6 +58,8 @@ else() if(NOT MSVC) target_compile_options(crashpad_zlib PRIVATE -msse4.2 -mpclmul) endif() + else() + target_sources(crashpad_zlib PRIVATE zlib/simd_stub.c) endif() target_compile_definitions(crashpad_zlib PUBLIC CRASHPAD_ZLIB_SOURCE_EMBEDDED From bce6d14d075f0b522c2744a186cae2c6c01f76dc Mon Sep 17 00:00:00 2001 From: Luke Street Date: Mon, 12 Apr 2021 04:27:42 -0400 Subject: [PATCH 045/146] fix: CMAKE_ASM_FLAGS with multiple OSX_ARCHITECTURES (#35) --- util/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index cb25aad393..84b5be6de8 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -429,7 +429,8 @@ if(APPLE) get_property(archs TARGET crashpad_util PROPERTY OSX_ARCHITECTURES) if (archs) list(TRANSFORM archs PREPEND "-arch ") - set(CMAKE_ASM_FLAGS "${CFLAGS} ${archs}") + list(JOIN archs " " archs_str) + set(CMAKE_ASM_FLAGS "${CFLAGS} ${archs_str}") endif() target_link_libraries(crashpad_util PRIVATE From deb4dceb4048c940ca98870c410d6fc03281a925 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 12 Apr 2021 16:14:21 +0200 Subject: [PATCH 046/146] Sync CMake and submodules --- client/CMakeLists.txt | 1 + third_party/mini_chromium/CMakeLists.txt | 2 -- third_party/mini_chromium/mini_chromium | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 11ba9f4edc..839476f664 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -30,6 +30,7 @@ if(IOS) target_sources(crashpad_client PRIVATE crash_report_database_mac.mm crashpad_client_ios.cc + simulate_crash_ios.h ) endif() diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 907ddc3bbf..b81cd3b098 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -51,8 +51,6 @@ mc_append_sources( scoped_clear_last_error.h scoped_generic.h stl_util.h - strings/string16.cc - strings/string16.h strings/string_number_conversions.cc strings/string_number_conversions.h strings/string_piece.h diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 12ea507eb7..329ca82f73 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 12ea507eb719a54698e1429e91e84c65284805ab +Subproject commit 329ca82f73a592d832e79334bed842fba85b9fdd From fdbe4d7b1053939f2c77193d8e854de5a3c93e17 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 20 Apr 2021 12:12:11 +0200 Subject: [PATCH 047/146] fix: Avoid EXPORT_NAME clash (#37) --- handler/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 073aff2093..36c77aa897 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -110,7 +110,7 @@ if(NOT IOS) endif() endif() - set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME handler) + set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME crashpad_handler) add_executable(crashpad::handler ALIAS crashpad_handler) install(TARGETS crashpad_handler EXPORT crashpad_export From 71bcaad4cf30294b8de1bfa02064ab629437163b Mon Sep 17 00:00:00 2001 From: Anonymous Maarten Date: Sun, 16 May 2021 14:47:25 +0200 Subject: [PATCH 048/146] fix: Add missing installed header (build/chromeos_buildflags.h) (#38) --- third_party/mini_chromium/CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index b81cd3b098..f8c85ea7cf 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -207,3 +207,7 @@ 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" +) From 5cf3032b2281cf0928acc8bccf69f91ccf26b939 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 14 Jun 2021 12:41:05 +0200 Subject: [PATCH 049/146] build: Sync CMake files with GN --- client/CMakeLists.txt | 2 +- handler/CMakeLists.txt | 8 ++++++-- snapshot/CMakeLists.txt | 26 +++++++++++++----------- third_party/mini_chromium/CMakeLists.txt | 2 +- third_party/mini_chromium/mini_chromium | 2 +- util/CMakeLists.txt | 17 ++++++++++++++++ 6 files changed, 40 insertions(+), 17 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 839476f664..702f002447 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -40,8 +40,8 @@ if(LINUX OR ANDROID) simulate_crash_linux.h client_argv_handling.cc client_argv_handling.h - crashpad_info_note.S crash_report_database_generic.cc + crashpad_info_note.S ) endif() diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 36c77aa897..75f8bbb41e 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -11,14 +11,18 @@ add_library(crashpad_handler_lib STATIC 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 - mac/file_limit_annotation.cc - mac/file_limit_annotation.h ) endif() diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index 5da894b76a..10679a1135 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -84,18 +84,20 @@ elseif(IOS) target_sources(crashpad_snapshot PRIVATE posix/timezone.cc posix/timezone.h - ios/exception_snapshot_ios.cc - ios/exception_snapshot_ios.h - ios/memory_snapshot_ios.cc - ios/memory_snapshot_ios.h - ios/module_snapshot_ios.cc - ios/module_snapshot_ios.h - ios/process_snapshot_ios.cc - ios/process_snapshot_ios.h - ios/system_snapshot_ios.cc - ios/system_snapshot_ios.h - ios/thread_snapshot_ios.cc - ios/thread_snapshot_ios.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 ) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index f8c85ea7cf..83a0ef875f 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -15,6 +15,7 @@ mc_append_sources( check.h check_op.h compiler_specific.h + cxx17_backports.h debug/alias.cc debug/alias.h files/file_path.cc @@ -50,7 +51,6 @@ mc_append_sources( rand_util.h scoped_clear_last_error.h scoped_generic.h - stl_util.h strings/string_number_conversions.cc strings/string_number_conversions.h strings/string_piece.h diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 329ca82f73..2470faf722 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 329ca82f73a592d832e79334bed842fba85b9fdd +Subproject commit 2470faf722b0fd259ca11c045fdb370e09037c4e diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 84b5be6de8..896d9ef0b7 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -204,8 +204,25 @@ if(APPLE) target_sources(crashpad_util PRIVATE ios/exception_processor.h ios/exception_processor.mm + ios/ios_intermediate_dump_data.cc + ios/ios_intermediate_dump_data.h + ios/ios_intermediate_dump_format.h + ios/ios_intermediate_dump_list.cc + ios/ios_intermediate_dump_list.h + ios/ios_intermediate_dump_map.cc + ios/ios_intermediate_dump_map.h + ios/ios_intermediate_dump_object.cc + ios/ios_intermediate_dump_object.h + ios/ios_intermediate_dump_reader.cc + ios/ios_intermediate_dump_reader.h + ios/ios_intermediate_dump_writer.cc + ios/ios_intermediate_dump_writer.h ios/ios_system_data_collector.h ios/ios_system_data_collector.mm + ios/raw_logging.cc + ios/raw_logging.h + ios/scoped_vm_read.cc + ios/scoped_vm_read.h ) endif() endif() From 7a31c8491ec771d7f7577c63585e2c4c4d49c03c Mon Sep 17 00:00:00 2001 From: Yuriy Levchenko Date: Wed, 14 Jul 2021 15:56:58 +0300 Subject: [PATCH 050/146] fix(mingw): Use correct char16 type for utf_string_conversion_utils (#41) --- .../mini_chromium/utf_string_conversion_utils.mingw.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc b/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc index 6baf7b452d..4184adf649 100644 --- a/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc +++ b/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc @@ -21,7 +21,7 @@ bool ReadUnicodeCharacter(const char* src, return IsValidCodepoint(code_point); } -bool ReadUnicodeCharacter(const char16* src, +bool ReadUnicodeCharacter(const char16_t* src, int32_t src_len, int32_t* char_index, uint32_t* code_point) { @@ -58,9 +58,9 @@ size_t WriteUnicodeCharacter(uint32_t code_point, std::string* output) { return char_offset - original_char_offset; } -size_t WriteUnicodeCharacter(uint32_t code_point, string16* output) { +size_t WriteUnicodeCharacter(uint32_t code_point, std::u16string* output) { if (CBU16_LENGTH(code_point) == 1) { - output->push_back(static_cast(code_point)); + output->push_back(static_cast(code_point)); return 1; } size_t char_offset = output->length(); @@ -84,7 +84,7 @@ void PrepareForUTF8Output(const CHAR* src, } template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*); -// template void PrepareForUTF8Output(const char16*, size_t, std::string*); +template void PrepareForUTF8Output(const char16_t*, size_t, std::string*); template void PrepareForUTF16Or32Output(const char* src, @@ -100,6 +100,6 @@ void PrepareForUTF16Or32Output(const char* src, } } -template void PrepareForUTF16Or32Output(const char*, size_t, string16*); +template void PrepareForUTF16Or32Output(const char*, size_t, std::u16string*); } // namespace base From 0d75b0631bddb5fdb012a143874e2577ec304546 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Wed, 28 Jul 2021 13:37:10 +0200 Subject: [PATCH 051/146] Update mini_chromium submodule --- third_party/mini_chromium/CMakeLists.txt | 6 +++--- third_party/mini_chromium/mini_chromium | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 83a0ef875f..216f3d477f 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -28,6 +28,7 @@ mc_append_sources( logging.h macros.h memory/free_deleter.h + memory/page_size.h memory/scoped_policy.h metrics/histogram_functions.h metrics/histogram_macros.h @@ -46,7 +47,6 @@ mc_append_sources( numerics/safe_math_shared_impl.h process/memory.cc process/memory.h - process/process_metrics.h rand_util.cc rand_util.h scoped_clear_last_error.h @@ -124,7 +124,7 @@ endif() if(WIN32) mc_append_sources( - process/process_metrics_win.cc + memory/page_size_win.cc scoped_clear_last_error_win.cc strings/string_util_win.cc strings/string_util_win.h @@ -134,10 +134,10 @@ if(WIN32) 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 - process/process_metrics_posix.cc strings/string_util_posix.h synchronization/condition_variable_posix.cc synchronization/lock_impl_posix.cc diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 2470faf722..f9ae4322df 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 2470faf722b0fd259ca11c045fdb370e09037c4e +Subproject commit f9ae4322dfc5bcc14cc82d6005107f3b75ca2059 From 59422f80649b513ae9b1ec0b3a7f97b5f51d44e2 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 6 Sep 2021 13:52:20 +0200 Subject: [PATCH 052/146] feat: Write client-side stacktraces to the minidump --- minidump/CMakeLists.txt | 2 + minidump/minidump_extensions.h | 15 ++- minidump/minidump_file_writer.cc | 11 ++- minidump/minidump_stacktrace_writer.cc | 121 +++++++++++++++++++++++++ minidump/minidump_stacktrace_writer.h | 102 +++++++++++++++++++++ 5 files changed, 244 insertions(+), 7 deletions(-) create mode 100644 minidump/minidump_stacktrace_writer.cc create mode 100644 minidump/minidump_stacktrace_writer.h diff --git a/minidump/CMakeLists.txt b/minidump/CMakeLists.txt index 1bf972ab98..e0b8f9a04e 100644 --- a/minidump/CMakeLists.txt +++ b/minidump/CMakeLists.txt @@ -30,6 +30,8 @@ add_library(crashpad_minidump STATIC minidump_rva_list_writer.h minidump_simple_string_dictionary_writer.cc minidump_simple_string_dictionary_writer.h + minidump_stacktrace_writer.cc + minidump_stacktrace_writer.h minidump_stream_writer.cc minidump_stream_writer.h minidump_string_writer.cc diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 97276d529a..21993eec1b 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -15,9 +15,9 @@ #ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ #define CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ -#include #include #include +#include #include #include "base/compiler_specific.h" @@ -32,7 +32,7 @@ // 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) +#pragma warning(disable : 4200) #define PACKED #pragma pack(push, 1) @@ -105,6 +105,14 @@ enum MinidumpStreamType : uint32_t { //! \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 @@ -439,8 +447,7 @@ struct ALIGNAS(4) PACKED MinidumpCrashpadInfo { report_id(), client_id(), simple_annotations(), - module_list() { - } + module_list() {} //! \brief The structure’s currently-defined version number. //! diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index 6727a0dcda..f12a84a401 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -24,8 +24,8 @@ #include "minidump/minidump_memory_writer.h" #include "minidump/minidump_misc_info_writer.h" #include "minidump/minidump_module_writer.h" +#include "minidump/minidump_stacktrace_writer.h" #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" @@ -51,8 +51,7 @@ MinidumpFileWriter::MinidumpFileWriter() header_.Flags = MiniDumpNormal; } -MinidumpFileWriter::~MinidumpFileWriter() { -} +MinidumpFileWriter::~MinidumpFileWriter() {} void MinidumpFileWriter::InitializeFromSnapshot( const ProcessSnapshot* process_snapshot) { @@ -104,6 +103,12 @@ void MinidumpFileWriter::InitializeFromSnapshot( add_stream_result = AddStream(std::move(module_list)); DCHECK(add_stream_result); + auto stacktrace_list = std::make_unique(); + stacktrace_list->InitializeFromSnapshot(process_snapshot->Threads(), + thread_id_map); + add_stream_result = AddStream(std::move(stacktrace_list)); + DCHECK(add_stream_result); + auto unloaded_modules = process_snapshot->UnloadedModules(); if (!unloaded_modules.empty()) { auto unloaded_module_list = diff --git a/minidump/minidump_stacktrace_writer.cc b/minidump/minidump_stacktrace_writer.cc new file mode 100644 index 0000000000..f9c69c9f59 --- /dev/null +++ b/minidump/minidump_stacktrace_writer.cc @@ -0,0 +1,121 @@ + +#include "minidump/minidump_stacktrace_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/thread_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 { + +MinidumpStacktraceListWriter::MinidumpStacktraceListWriter() + : MinidumpStreamWriter(), + threads_(), + frames_(), + symbol_bytes_(), + stacktrace_header_() {} + +MinidumpStacktraceListWriter::~MinidumpStacktraceListWriter() {} + +void MinidumpStacktraceListWriter::InitializeFromSnapshot( + const std::vector& thread_snapshots, + const MinidumpThreadIDMap& thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + + DCHECK(threads_.empty()); + DCHECK(frames_.empty()); + DCHECK(symbol_bytes_.empty()); + + for (auto thread_snapshot : thread_snapshots) { + internal::RawThread thread; + thread.thread_id = thread_snapshot->ThreadID(); + thread.start_frame = frames_.size(); + + // TODO: Create a stub that will later return a real stack trace: + // That would be https://getsentry.atlassian.net/browse/NATIVE-198 + // auto frames = thread_snapshot->StackTrace(); + std::vector frames; + frames.emplace_back(0xfff70001, std::string("uiaeo")); + frames.emplace_back(0xfff70002, std::string("snrtdy")); + + for (auto frame_snapshot : frames) { + internal::RawFrame frame; + frame.instruction_addr = frame_snapshot.InstructionAddr(); + frame.symbol_offset = 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 = symbol.size(); + + frames_.push_back(frame); + } + + thread.num_frames = frames_.size() - thread.start_frame; + + threads_.push_back(thread); + } + + stacktrace_header_.version = 1; + stacktrace_header_.num_threads = threads_.size(); + stacktrace_header_.num_frames = frames_.size(); + stacktrace_header_.symbol_bytes = symbol_bytes_.size(); +} + +size_t MinidumpStacktraceListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(stacktrace_header_) + + threads_.size() * sizeof(internal::RawThread) + + frames_.size() * sizeof(internal::RawFrame) + 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); + + 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); + + iov.iov_base = &threads_.front(); + iov.iov_len = threads_.size() * sizeof(internal::RawThread); + iovecs.push_back(iov); + + iov.iov_base = &frames_.front(); + iov.iov_len = frames_.size() * sizeof(internal::RawFrame); + iovecs.push_back(iov); + + 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/minidump/minidump_stacktrace_writer.h b/minidump/minidump_stacktrace_writer.h new file mode 100644 index 0000000000..d7816f9e18 --- /dev/null +++ b/minidump/minidump_stacktrace_writer.h @@ -0,0 +1,102 @@ +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STACKTRACE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_STACKTRACE_WRITER_H_ + +#include +#include + +#include +#include +#include + +#include "base/macros.h" +#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 { + uint64_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 + +// TODO: Create a stub that will later return a real stack trace: +// `ThreadSnapshot.StackTrace` would need to return a +// `const std::vector&`, so that followup will also move +// that type to a more appropriate place. +// That would be https://getsentry.atlassian.net/browse/NATIVE-198 +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_; +}; + +class ThreadSnapshot; + +//! \brief The writer for our custom client-side stacktraces stream in a +//! minidump file. +class MinidumpStacktraceListWriter final + : public internal::MinidumpStreamWriter { + public: + MinidumpStacktraceListWriter(); + ~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); + + 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_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpStacktraceListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_STACKTRACE_WRITER_H_ From 19176d1babc51fd3cf82c125d964439d9e099a7e Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 7 Sep 2021 11:18:17 +0200 Subject: [PATCH 053/146] update cmake and submodules --- client/CMakeLists.txt | 4 ++++ third_party/mini_chromium/mini_chromium | 2 +- util/CMakeLists.txt | 2 -- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 702f002447..5357c8624f 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -30,6 +30,10 @@ 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_intermediate_dump_handler.cc + ios_handler/in_process_intermediate_dump_handler.h simulate_crash_ios.h ) endif() diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index f9ae4322df..8f7a60f2c6 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit f9ae4322dfc5bcc14cc82d6005107f3b75ca2059 +Subproject commit 8f7a60f2c637f2a3c5d25f320739b3de7c2e325d diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 896d9ef0b7..28218b856e 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -202,8 +202,6 @@ if(APPLE) ) else() target_sources(crashpad_util PRIVATE - ios/exception_processor.h - ios/exception_processor.mm ios/ios_intermediate_dump_data.cc ios/ios_intermediate_dump_data.h ios/ios_intermediate_dump_format.h From a47690d05ae493fb877276b1e8b36b7ff14d01d8 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 7 Sep 2021 15:07:44 +0200 Subject: [PATCH 054/146] revert automatic include formatting --- minidump/minidump_extensions.h | 7 ++++--- minidump/minidump_file_writer.cc | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 21993eec1b..db0c33ec5e 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -15,9 +15,9 @@ #ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ #define CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ +#include #include #include -#include #include #include "base/compiler_specific.h" @@ -32,7 +32,7 @@ // 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) +#pragma warning(disable: 4200) #define PACKED #pragma pack(push, 1) @@ -447,7 +447,8 @@ struct ALIGNAS(4) PACKED MinidumpCrashpadInfo { report_id(), client_id(), simple_annotations(), - module_list() {} + module_list() { + } //! \brief The structure’s currently-defined version number. //! diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index f12a84a401..8cd23f241f 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -26,6 +26,7 @@ #include "minidump/minidump_module_writer.h" #include "minidump/minidump_stacktrace_writer.h" #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" From 6dd26ef75c1f477b63459ac5599ff2edab12c4be Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 7 Sep 2021 15:35:15 +0200 Subject: [PATCH 055/146] remove unused includes, explicitly cast indices and nums --- minidump/minidump_stacktrace_writer.cc | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/minidump/minidump_stacktrace_writer.cc b/minidump/minidump_stacktrace_writer.cc index f9c69c9f59..dd79261ba5 100644 --- a/minidump/minidump_stacktrace_writer.cc +++ b/minidump/minidump_stacktrace_writer.cc @@ -7,14 +7,8 @@ #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/thread_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 { @@ -39,7 +33,7 @@ void MinidumpStacktraceListWriter::InitializeFromSnapshot( for (auto thread_snapshot : thread_snapshots) { internal::RawThread thread; thread.thread_id = thread_snapshot->ThreadID(); - thread.start_frame = frames_.size(); + thread.start_frame = (uint32_t)frames_.size(); // TODO: Create a stub that will later return a real stack trace: // That would be https://getsentry.atlassian.net/browse/NATIVE-198 @@ -51,27 +45,27 @@ void MinidumpStacktraceListWriter::InitializeFromSnapshot( for (auto frame_snapshot : frames) { internal::RawFrame frame; frame.instruction_addr = frame_snapshot.InstructionAddr(); - frame.symbol_offset = symbol_bytes_.size(); + 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 = symbol.size(); + frame.symbol_len = (uint32_t)symbol.size(); frames_.push_back(frame); } - thread.num_frames = frames_.size() - thread.start_frame; + thread.num_frames = (uint32_t)frames_.size() - thread.start_frame; threads_.push_back(thread); } stacktrace_header_.version = 1; - stacktrace_header_.num_threads = threads_.size(); - stacktrace_header_.num_frames = frames_.size(); - stacktrace_header_.symbol_bytes = symbol_bytes_.size(); + 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() { From 0bbcdfd2c490e96a9466ed93a8bded9f96cd9f70 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 7 Sep 2021 16:27:15 +0200 Subject: [PATCH 056/146] Make thread_id 32bits and fix alignment --- minidump/minidump_stacktrace_writer.cc | 45 +++++++++++++++++++++++--- minidump/minidump_stacktrace_writer.h | 2 +- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/minidump/minidump_stacktrace_writer.cc b/minidump/minidump_stacktrace_writer.cc index dd79261ba5..df8c703680 100644 --- a/minidump/minidump_stacktrace_writer.cc +++ b/minidump/minidump_stacktrace_writer.cc @@ -12,6 +12,15 @@ 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_(), @@ -32,7 +41,10 @@ void MinidumpStacktraceListWriter::InitializeFromSnapshot( for (auto thread_snapshot : thread_snapshots) { internal::RawThread thread; - thread.thread_id = thread_snapshot->ThreadID(); + + 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(); // TODO: Create a stub that will later return a real stack trace: @@ -71,9 +83,14 @@ void MinidumpStacktraceListWriter::InitializeFromSnapshot( size_t MinidumpStacktraceListWriter::SizeOfObject() { DCHECK_GE(state(), kStateFrozen); - return sizeof(stacktrace_header_) + - threads_.size() * sizeof(internal::RawThread) + - frames_.size() * sizeof(internal::RawFrame) + symbol_bytes_.size(); + 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() { @@ -85,6 +102,7 @@ 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); @@ -93,14 +111,33 @@ bool MinidumpStacktraceListWriter::WriteObject( 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); + } + 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); + } + 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); + } + iov.iov_base = &symbol_bytes_.front(); iov.iov_len = symbol_bytes_.size(); iovecs.push_back(iov); diff --git a/minidump/minidump_stacktrace_writer.h b/minidump/minidump_stacktrace_writer.h index d7816f9e18..b6984acb67 100644 --- a/minidump/minidump_stacktrace_writer.h +++ b/minidump/minidump_stacktrace_writer.h @@ -26,7 +26,7 @@ struct Header { }; struct RawThread { - uint64_t thread_id; + uint32_t thread_id; uint32_t start_frame; uint32_t num_frames; }; From 1fa28f4edb6c3dcb7d611c0fc7f730d0cdb423bd Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 8 Sep 2021 15:25:05 +0200 Subject: [PATCH 057/146] Remove dummy frames --- minidump/minidump_stacktrace_writer.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/minidump/minidump_stacktrace_writer.cc b/minidump/minidump_stacktrace_writer.cc index df8c703680..e85ae183b5 100644 --- a/minidump/minidump_stacktrace_writer.cc +++ b/minidump/minidump_stacktrace_writer.cc @@ -51,8 +51,6 @@ void MinidumpStacktraceListWriter::InitializeFromSnapshot( // That would be https://getsentry.atlassian.net/browse/NATIVE-198 // auto frames = thread_snapshot->StackTrace(); std::vector frames; - frames.emplace_back(0xfff70001, std::string("uiaeo")); - frames.emplace_back(0xfff70002, std::string("snrtdy")); for (auto frame_snapshot : frames) { internal::RawFrame frame; From a95e88e031e3665ae0a6d06b05ad3737671f3f60 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Mon, 6 Sep 2021 15:16:34 +0200 Subject: [PATCH 058/146] Add StackTrace method to ThreadSnapshot --- minidump/minidump_stacktrace_writer.cc | 5 +--- minidump/minidump_stacktrace_writer.h | 18 --------------- snapshot/thread_snapshot.h | 32 ++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/minidump/minidump_stacktrace_writer.cc b/minidump/minidump_stacktrace_writer.cc index e85ae183b5..cc1bb70eb6 100644 --- a/minidump/minidump_stacktrace_writer.cc +++ b/minidump/minidump_stacktrace_writer.cc @@ -47,10 +47,7 @@ void MinidumpStacktraceListWriter::InitializeFromSnapshot( thread.thread_id = thread_id_it->second; thread.start_frame = (uint32_t)frames_.size(); - // TODO: Create a stub that will later return a real stack trace: - // That would be https://getsentry.atlassian.net/browse/NATIVE-198 - // auto frames = thread_snapshot->StackTrace(); - std::vector frames; + std::vector frames = thread_snapshot->StackTrace(); for (auto frame_snapshot : frames) { internal::RawFrame frame; diff --git a/minidump/minidump_stacktrace_writer.h b/minidump/minidump_stacktrace_writer.h index b6984acb67..e1a4963ca5 100644 --- a/minidump/minidump_stacktrace_writer.h +++ b/minidump/minidump_stacktrace_writer.h @@ -39,24 +39,6 @@ struct RawFrame { } // namespace internal -// TODO: Create a stub that will later return a real stack trace: -// `ThreadSnapshot.StackTrace` would need to return a -// `const std::vector&`, so that followup will also move -// that type to a more appropriate place. -// That would be https://getsentry.atlassian.net/browse/NATIVE-198 -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_; -}; - class ThreadSnapshot; //! \brief The writer for our custom client-side stacktraces stream in a diff --git a/snapshot/thread_snapshot.h b/snapshot/thread_snapshot.h index 4d732578e5..37791e00af 100644 --- a/snapshot/thread_snapshot.h +++ b/snapshot/thread_snapshot.h @@ -24,10 +24,33 @@ namespace crashpad { struct CPUContext; class MemorySnapshot; +// TODO: Create a stub that will later return a real stack trace: +// `ThreadSnapshot.StackTrace` would need to return a +// `const std::vector&`. +// That would be https://getsentry.atlassian.net/browse/NATIVE-198 +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_; +}; + //! \brief An abstract interface to a snapshot representing a thread //! (lightweight process) present in a snapshot process. class ThreadSnapshot { public: + ThreadSnapshot() { + frames_ = std::vector(); + frames_.emplace_back(0xfff70001, std::string("uiaeo")); + frames_.emplace_back(0xfff70002, std::string("snrtdy")); + } + virtual ~ThreadSnapshot() {} //! \brief Returns a CPUContext object corresponding to the thread’s CPU @@ -73,6 +96,15 @@ class ThreadSnapshot { //! are scoped to the lifetime of the ThreadSnapshot object that they //! were obtained from. virtual std::vector ExtraMemory() const = 0; + + // TODO: This should return a `const std::vector&`. + const std::vector& StackTrace() const { + return frames_; + } + + private: + std::vector frames_; + }; } // namespace crashpad From 894ba6414bc9d94ca559780b47b120ac10c33894 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 7 Sep 2021 11:11:51 +0200 Subject: [PATCH 059/146] Add missing include --- snapshot/thread_snapshot.h | 1 + 1 file changed, 1 insertion(+) diff --git a/snapshot/thread_snapshot.h b/snapshot/thread_snapshot.h index 37791e00af..c567aac81c 100644 --- a/snapshot/thread_snapshot.h +++ b/snapshot/thread_snapshot.h @@ -18,6 +18,7 @@ #include #include +#include namespace crashpad { From 39cf7b17e370dc723b349288c4ccb75b8b5f11b6 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 7 Sep 2021 15:47:14 +0200 Subject: [PATCH 060/146] Remove obsolete comment --- snapshot/thread_snapshot.h | 1 - 1 file changed, 1 deletion(-) diff --git a/snapshot/thread_snapshot.h b/snapshot/thread_snapshot.h index c567aac81c..09f5512bfc 100644 --- a/snapshot/thread_snapshot.h +++ b/snapshot/thread_snapshot.h @@ -98,7 +98,6 @@ class ThreadSnapshot { //! were obtained from. virtual std::vector ExtraMemory() const = 0; - // TODO: This should return a `const std::vector&`. const std::vector& StackTrace() const { return frames_; } From a61bfefd5a58e24a3a22aa9d6cd37daba59017d6 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 8 Sep 2021 15:24:21 +0200 Subject: [PATCH 061/146] Remove dummy frames --- snapshot/thread_snapshot.h | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/snapshot/thread_snapshot.h b/snapshot/thread_snapshot.h index 09f5512bfc..8606e42403 100644 --- a/snapshot/thread_snapshot.h +++ b/snapshot/thread_snapshot.h @@ -46,11 +46,7 @@ class FrameSnapshot { //! (lightweight process) present in a snapshot process. class ThreadSnapshot { public: - ThreadSnapshot() { - frames_ = std::vector(); - frames_.emplace_back(0xfff70001, std::string("uiaeo")); - frames_.emplace_back(0xfff70002, std::string("snrtdy")); - } + ThreadSnapshot() : frames_() {} virtual ~ThreadSnapshot() {} @@ -102,7 +98,7 @@ class ThreadSnapshot { return frames_; } - private: + protected: std::vector frames_; }; From 777fb6ae933f53a6d04ea1b28f749bb8f18684d8 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Wed, 8 Sep 2021 16:29:10 +0200 Subject: [PATCH 062/146] Remove comment --- snapshot/thread_snapshot.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/snapshot/thread_snapshot.h b/snapshot/thread_snapshot.h index 8606e42403..0251fbfca6 100644 --- a/snapshot/thread_snapshot.h +++ b/snapshot/thread_snapshot.h @@ -25,10 +25,6 @@ namespace crashpad { struct CPUContext; class MemorySnapshot; -// TODO: Create a stub that will later return a real stack trace: -// `ThreadSnapshot.StackTrace` would need to return a -// `const std::vector&`. -// That would be https://getsentry.atlassian.net/browse/NATIVE-198 class FrameSnapshot { public: FrameSnapshot(uint64_t instruction_addr, std::string symbol) From 725271e15e424cc79a5d528eee5b2806c8a502ac Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 11 Oct 2021 14:07:03 +0200 Subject: [PATCH 063/146] feat: Do client-side stackwalking on Windows (NATIVE-150) (#46) --- minidump/minidump_file_writer.cc | 4 +- minidump/minidump_stacktrace_writer.cc | 56 +++++++++---- minidump/minidump_stacktrace_writer.h | 4 +- snapshot/CMakeLists.txt | 2 +- snapshot/win/process_reader_win.cc | 104 ++++++++++++++++++++++--- snapshot/win/process_reader_win.h | 3 + snapshot/win/thread_snapshot_win.cc | 8 +- 7 files changed, 145 insertions(+), 36 deletions(-) diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index 8cd23f241f..deb2c86f78 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -105,8 +105,8 @@ void MinidumpFileWriter::InitializeFromSnapshot( DCHECK(add_stream_result); auto stacktrace_list = std::make_unique(); - stacktrace_list->InitializeFromSnapshot(process_snapshot->Threads(), - thread_id_map); + stacktrace_list->InitializeFromSnapshot( + process_snapshot->Threads(), thread_id_map, exception_snapshot); add_stream_result = AddStream(std::move(stacktrace_list)); DCHECK(add_stream_result); diff --git a/minidump/minidump_stacktrace_writer.cc b/minidump/minidump_stacktrace_writer.cc index cc1bb70eb6..74724ca773 100644 --- a/minidump/minidump_stacktrace_writer.cc +++ b/minidump/minidump_stacktrace_writer.cc @@ -7,6 +7,7 @@ #include #include "base/logging.h" +#include "snapshot/exception_snapshot.h" #include "snapshot/thread_snapshot.h" #include "util/file/file_writer.h" @@ -32,7 +33,8 @@ MinidumpStacktraceListWriter::~MinidumpStacktraceListWriter() {} void MinidumpStacktraceListWriter::InitializeFromSnapshot( const std::vector& thread_snapshots, - const MinidumpThreadIDMap& thread_id_map) { + const MinidumpThreadIDMap& thread_id_map, + const ExceptionSnapshot* exception_snapshot) { DCHECK_EQ(state(), kStateMutable); DCHECK(threads_.empty()); @@ -49,6 +51,20 @@ void MinidumpStacktraceListWriter::InitializeFromSnapshot( 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(); @@ -110,32 +126,38 @@ bool MinidumpStacktraceListWriter::WriteObject( iov.iov_base = &padding; iov.iov_len = align_to_8(iov.iov_len); if (iov.iov_len > 0) { - iovecs.push_back(iov); + iovecs.push_back(iov); } - iov.iov_base = &threads_.front(); - iov.iov_len = threads_.size() * sizeof(internal::RawThread); - 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) { + iov.iov_base = &padding; + iov.iov_len = align_to_8(iov.iov_len); + if (iov.iov_len > 0) { iovecs.push_back(iov); + } } - iov.iov_base = &frames_.front(); - iov.iov_len = frames_.size() * sizeof(internal::RawFrame); - 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) { + iov.iov_base = &padding; + iov.iov_len = align_to_8(iov.iov_len); + if (iov.iov_len > 0) { iovecs.push_back(iov); + } } - iov.iov_base = &symbol_bytes_.front(); - iov.iov_len = symbol_bytes_.size(); - 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); } diff --git a/minidump/minidump_stacktrace_writer.h b/minidump/minidump_stacktrace_writer.h index e1a4963ca5..cddea4e7db 100644 --- a/minidump/minidump_stacktrace_writer.h +++ b/minidump/minidump_stacktrace_writer.h @@ -40,6 +40,7 @@ struct RawFrame { } // namespace internal class ThreadSnapshot; +class ExceptionSnapshot; //! \brief The writer for our custom client-side stacktraces stream in a //! minidump file. @@ -57,7 +58,8 @@ class MinidumpStacktraceListWriter final //! identified by \a thread_snapshots. void InitializeFromSnapshot( const std::vector& thread_snapshots, - const MinidumpThreadIDMap& thread_id_map); + const MinidumpThreadIDMap& thread_id_map, + const ExceptionSnapshot* exception_snapshot); protected: // MinidumpWritable: diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index 10679a1135..73b8f004b5 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -202,7 +202,7 @@ target_link_libraries(crashpad_snapshot ) if(WIN32) - target_link_libraries(crashpad_snapshot PRIVATE powrprof) + target_link_libraries(crashpad_snapshot PRIVATE powrprof dbghelp) if(MSVC) target_compile_options(crashpad_snapshot PRIVATE "/wd4201") endif() diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index e3784caefc..d88f393598 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -14,6 +14,7 @@ #include "snapshot/win/process_reader_win.h" +#include #include #include @@ -125,25 +126,102 @@ HANDLE OpenThread( return handle; } +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); + } +} + // 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 thread_handle, +bool FillThreadContextAndSuspendCount(HANDLE process, + HANDLE thread_handle, ProcessReaderWin::Thread* thread, ProcessSuspensionState suspension_state, bool is_64_reading_32) { // 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; + 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); + + DoStackWalk(thread, process, thread_handle, is_64_reading_32); } else { DWORD previous_suspend_count = SuspendThread(thread_handle); if (previous_suspend_count == static_cast(-1)) { @@ -183,6 +261,8 @@ bool FillThreadContextAndSuspendCount(HANDLE thread_handle, } } + DoStackWalk(thread, process, thread_handle, is_64_reading_32); + if (!ResumeThread(thread_handle)) { PLOG(ERROR) << "ResumeThread"; return false; @@ -203,8 +283,7 @@ ProcessReaderWin::Thread::Thread() stack_region_size(0), suspend_count(0), priority_class(0), - priority(0) { -} + priority(0) {} ProcessReaderWin::ProcessReaderWin() : process_(INVALID_HANDLE_VALUE), @@ -214,11 +293,9 @@ ProcessReaderWin::ProcessReaderWin() modules_(), suspension_state_(), initialized_threads_(false), - initialized_() { -} + initialized_() {} -ProcessReaderWin::~ProcessReaderWin() { -} +ProcessReaderWin::~ProcessReaderWin() {} bool ProcessReaderWin::Initialize(HANDLE process, ProcessSuspensionState suspension_state) { @@ -309,6 +386,10 @@ void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { if (!process_information) return; + DWORD options = SymGetOptions(); + SymSetOptions(options | SYMOPT_UNDNAME); + SymInitialize(process_, NULL, TRUE); + for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { const process_types::SYSTEM_THREAD_INFORMATION& thread_info = process_information->Threads[i]; @@ -319,7 +400,8 @@ void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { if (!thread_handle.is_valid()) continue; - if (!FillThreadContextAndSuspendCount(thread_handle.get(), + if (!FillThreadContextAndSuspendCount(process_, + thread_handle.get(), &thread, suspension_state_, is_64_reading_32)) { diff --git a/snapshot/win/process_reader_win.h b/snapshot/win/process_reader_win.h index a4e32aaf83..92f54a1441 100644 --- a/snapshot/win/process_reader_win.h +++ b/snapshot/win/process_reader_win.h @@ -22,6 +22,7 @@ #include "base/macros.h" #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" @@ -60,6 +61,8 @@ class ProcessReaderWin { uint32_t suspend_count; uint32_t priority_class; uint32_t priority; + + std::vector frames; }; ProcessReaderWin(); diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc index c3894a78b1..e2bdf7e3c5 100644 --- a/snapshot/win/thread_snapshot_win.cc +++ b/snapshot/win/thread_snapshot_win.cc @@ -32,11 +32,9 @@ ThreadSnapshotWin::ThreadSnapshotWin() stack_(), teb_(), thread_(), - initialized_() { -} + initialized_() {} -ThreadSnapshotWin::~ThreadSnapshotWin() { -} +ThreadSnapshotWin::~ThreadSnapshotWin() {} bool ThreadSnapshotWin::Initialize( ProcessReaderWin* process_reader, @@ -86,6 +84,8 @@ bool ThreadSnapshotWin::Initialize( #error Unsupported Windows Arch #endif // ARCH_CPU_X86 + frames_ = thread_.frames; + CaptureMemoryDelegateWin capture_memory_delegate( process_reader, thread_, From 2743206d0a52cf11730a14ca139113c679e24edd Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 11 Oct 2021 14:12:04 +0200 Subject: [PATCH 064/146] feat: Make client-side stack traces opt-in (NATIVE-262) (#48) --- CMakeLists.txt | 1 + minidump/CMakeLists.txt | 10 ++++++++-- minidump/minidump_file_writer.cc | 4 ++++ snapshot/CMakeLists.txt | 9 ++++++++- snapshot/thread_snapshot.h | 19 ++++++++++++------- snapshot/win/process_reader_win.cc | 14 ++++++++++++++ snapshot/win/process_reader_win.h | 2 ++ snapshot/win/thread_snapshot_win.cc | 2 ++ 8 files changed, 51 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index abb06971a6..7d7a479540 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,7 @@ 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) diff --git a/minidump/CMakeLists.txt b/minidump/CMakeLists.txt index e0b8f9a04e..b964add2a7 100644 --- a/minidump/CMakeLists.txt +++ b/minidump/CMakeLists.txt @@ -30,8 +30,6 @@ add_library(crashpad_minidump STATIC minidump_rva_list_writer.h minidump_simple_string_dictionary_writer.cc minidump_simple_string_dictionary_writer.h - minidump_stacktrace_writer.cc - minidump_stacktrace_writer.h minidump_stream_writer.cc minidump_stream_writer.h minidump_string_writer.cc @@ -64,6 +62,14 @@ target_link_libraries(crashpad_minidump 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() diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index deb2c86f78..99ee2da06e 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -24,7 +24,9 @@ #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" @@ -104,11 +106,13 @@ void MinidumpFileWriter::InitializeFromSnapshot( 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()) { diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index 73b8f004b5..c684793993 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -201,8 +201,15 @@ target_link_libraries(crashpad_snapshot 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 dbghelp) + 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() diff --git a/snapshot/thread_snapshot.h b/snapshot/thread_snapshot.h index 0251fbfca6..08604a8836 100644 --- a/snapshot/thread_snapshot.h +++ b/snapshot/thread_snapshot.h @@ -17,14 +17,17 @@ #include -#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) @@ -37,12 +40,15 @@ class FrameSnapshot { 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() {} @@ -90,13 +96,12 @@ class ThreadSnapshot { //! were obtained from. virtual std::vector ExtraMemory() const = 0; - const std::vector& StackTrace() const { - return frames_; - } - - protected: - std::vector frames_; +#ifdef CLIENT_STACKTRACES_ENABLED + const std::vector& StackTrace() const { return frames_; } + protected: + std::vector frames_; +#endif }; } // namespace crashpad diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index d88f393598..ff4a23615b 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -14,7 +14,9 @@ #include "snapshot/win/process_reader_win.h" +#ifdef CLIENT_STACKTRACES_ENABLED #include +#endif #include #include @@ -126,6 +128,7 @@ HANDLE OpenThread( return handle; } +#ifdef CLIENT_STACKTRACES_ENABLED void DoStackWalk(ProcessReaderWin::Thread* thread, HANDLE process, HANDLE thread_handle, @@ -198,6 +201,7 @@ void DoStackWalk(ProcessReaderWin::Thread* thread, 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 @@ -208,6 +212,10 @@ bool FillThreadContextAndSuspendCount(HANDLE process, 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 = @@ -221,7 +229,9 @@ bool FillThreadContextAndSuspendCount(HANDLE process, 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)) { @@ -261,7 +271,9 @@ bool FillThreadContextAndSuspendCount(HANDLE process, } } +#ifdef CLIENT_STACKTRACES_ENABLED DoStackWalk(thread, process, thread_handle, is_64_reading_32); +#endif if (!ResumeThread(thread_handle)) { PLOG(ERROR) << "ResumeThread"; @@ -386,9 +398,11 @@ void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { 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 = diff --git a/snapshot/win/process_reader_win.h b/snapshot/win/process_reader_win.h index 92f54a1441..c033cf7687 100644 --- a/snapshot/win/process_reader_win.h +++ b/snapshot/win/process_reader_win.h @@ -62,7 +62,9 @@ class ProcessReaderWin { uint32_t priority_class; uint32_t priority; +#ifdef CLIENT_STACKTRACES_ENABLED std::vector frames; +#endif }; ProcessReaderWin(); diff --git a/snapshot/win/thread_snapshot_win.cc b/snapshot/win/thread_snapshot_win.cc index e2bdf7e3c5..b891b05b94 100644 --- a/snapshot/win/thread_snapshot_win.cc +++ b/snapshot/win/thread_snapshot_win.cc @@ -84,7 +84,9 @@ bool ThreadSnapshotWin::Initialize( #error Unsupported Windows Arch #endif // ARCH_CPU_X86 +#ifdef CLIENT_STACKTRACES_ENABLED frames_ = thread_.frames; +#endif CaptureMemoryDelegateWin capture_memory_delegate( process_reader, From 94945e7724000ff915c8acaf1fc355f413ce3f36 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 18 Oct 2021 12:37:30 +0200 Subject: [PATCH 065/146] meta: Build client-side stack walking code in CI (#51) --- .github/workflows/build.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5211b0776..ce12943427 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,10 +17,22 @@ jobs: - uses: actions/checkout@v2 with: submodules: "recursive" - - uses: lukka/run-cmake@v2 - with: - cmakeListsOrSettingsJson: "CMakeListsTxtAdvanced" - buildWithCMakeArgs: "--parallel" + + - name: Installing Linux Dependencies + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt update + sudo apt install zlib1g-dev libcurl4-openssl-dev libssl-dev libunwind-dev + + - 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 From 4be14116a305d9283c9019fb3fe6540a902fc860 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 18 Oct 2021 12:40:42 +0200 Subject: [PATCH 066/146] meta: Vendor libunwind (#50) This just copied the source of `libunwind` from llvm-project, without any changes. --- libunwind/.clang-format | 2 + libunwind/CMakeLists.txt | 373 ++ libunwind/LICENSE.TXT | 311 ++ .../cmake/Modules/HandleCompilerRT.cmake | 64 + .../cmake/Modules/HandleLibunwindFlags.cmake | 283 ++ libunwind/cmake/config-ix.cmake | 105 + libunwind/docs/BuildingLibunwind.rst | 168 + libunwind/docs/CMakeLists.txt | 7 + libunwind/docs/README.txt | 13 + libunwind/docs/conf.py | 252 + libunwind/docs/index.rst | 104 + libunwind/include/__libunwind_config.h | 176 + libunwind/include/libunwind.h | 1176 +++++ .../include/mach-o/compact_unwind_encoding.h | 477 ++ libunwind/include/unwind.h | 207 + libunwind/include/unwind_arm_ehabi.h | 169 + libunwind/include/unwind_itanium.h | 76 + libunwind/src/AddressSpace.hpp | 630 +++ libunwind/src/CMakeLists.txt | 211 + libunwind/src/CompactUnwinder.hpp | 697 +++ libunwind/src/DwarfInstructions.hpp | 838 +++ libunwind/src/DwarfParser.hpp | 813 +++ libunwind/src/EHHeaderParser.hpp | 169 + libunwind/src/FrameHeaderCache.hpp | 149 + libunwind/src/RWMutex.hpp | 114 + libunwind/src/Registers.hpp | 4509 +++++++++++++++++ libunwind/src/Unwind-EHABI.cpp | 1141 +++++ libunwind/src/Unwind-EHABI.h | 50 + libunwind/src/Unwind-seh.cpp | 491 ++ libunwind/src/Unwind-sjlj.c | 528 ++ libunwind/src/UnwindCursor.hpp | 2146 ++++++++ libunwind/src/UnwindLevel1-gcc-ext.c | 317 ++ libunwind/src/UnwindLevel1.c | 561 ++ libunwind/src/UnwindRegistersRestore.S | 1167 +++++ libunwind/src/UnwindRegistersSave.S | 1117 ++++ libunwind/src/Unwind_AppleExtras.cpp | 113 + libunwind/src/assembly.h | 230 + libunwind/src/cet_unwind.h | 41 + libunwind/src/config.h | 241 + libunwind/src/dwarf2.h | 239 + libunwind/src/libunwind.cpp | 341 ++ libunwind/src/libunwind_ext.h | 65 + libunwind/test/CMakeLists.txt | 61 + libunwind/test/alignment.compile.pass.cpp | 24 + libunwind/test/floatregister.pass.cpp | 51 + libunwind/test/forceunwind.pass.cpp | 74 + libunwind/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 + libunwind/test/lit.cfg.py | 10 + libunwind/test/lit.site.cfg.in | 58 + libunwind/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 + libunwind/test/unwind_leaffunction.pass.cpp | 57 + 59 files changed, 21726 insertions(+) create mode 100644 libunwind/.clang-format create mode 100644 libunwind/CMakeLists.txt create mode 100644 libunwind/LICENSE.TXT create mode 100644 libunwind/cmake/Modules/HandleCompilerRT.cmake create mode 100644 libunwind/cmake/Modules/HandleLibunwindFlags.cmake create mode 100644 libunwind/cmake/config-ix.cmake create mode 100644 libunwind/docs/BuildingLibunwind.rst create mode 100644 libunwind/docs/CMakeLists.txt create mode 100644 libunwind/docs/README.txt create mode 100644 libunwind/docs/conf.py create mode 100644 libunwind/docs/index.rst create mode 100644 libunwind/include/__libunwind_config.h create mode 100644 libunwind/include/libunwind.h create mode 100644 libunwind/include/mach-o/compact_unwind_encoding.h create mode 100644 libunwind/include/unwind.h create mode 100644 libunwind/include/unwind_arm_ehabi.h create mode 100644 libunwind/include/unwind_itanium.h create mode 100644 libunwind/src/AddressSpace.hpp create mode 100644 libunwind/src/CMakeLists.txt create mode 100644 libunwind/src/CompactUnwinder.hpp create mode 100644 libunwind/src/DwarfInstructions.hpp create mode 100644 libunwind/src/DwarfParser.hpp create mode 100644 libunwind/src/EHHeaderParser.hpp create mode 100644 libunwind/src/FrameHeaderCache.hpp create mode 100644 libunwind/src/RWMutex.hpp create mode 100644 libunwind/src/Registers.hpp create mode 100644 libunwind/src/Unwind-EHABI.cpp create mode 100644 libunwind/src/Unwind-EHABI.h create mode 100644 libunwind/src/Unwind-seh.cpp create mode 100644 libunwind/src/Unwind-sjlj.c create mode 100644 libunwind/src/UnwindCursor.hpp create mode 100644 libunwind/src/UnwindLevel1-gcc-ext.c create mode 100644 libunwind/src/UnwindLevel1.c create mode 100644 libunwind/src/UnwindRegistersRestore.S create mode 100644 libunwind/src/UnwindRegistersSave.S create mode 100644 libunwind/src/Unwind_AppleExtras.cpp create mode 100644 libunwind/src/assembly.h create mode 100644 libunwind/src/cet_unwind.h create mode 100644 libunwind/src/config.h create mode 100644 libunwind/src/dwarf2.h create mode 100644 libunwind/src/libunwind.cpp create mode 100644 libunwind/src/libunwind_ext.h create mode 100644 libunwind/test/CMakeLists.txt create mode 100644 libunwind/test/alignment.compile.pass.cpp create mode 100644 libunwind/test/floatregister.pass.cpp create mode 100644 libunwind/test/forceunwind.pass.cpp create mode 100644 libunwind/test/frameheadercache_test.pass.cpp create mode 100644 libunwind/test/libunwind/__init__.py create mode 100644 libunwind/test/libunwind/test/__init__.py create mode 100644 libunwind/test/libunwind/test/config.py create mode 100644 libunwind/test/libunwind_01.pass.cpp create mode 100644 libunwind/test/libunwind_02.pass.cpp create mode 100644 libunwind/test/lit.cfg.py create mode 100644 libunwind/test/lit.site.cfg.in create mode 100644 libunwind/test/remember_state_leak.pass.sh.s create mode 100644 libunwind/test/signal_frame.pass.cpp create mode 100644 libunwind/test/signal_unwind.pass.cpp create mode 100644 libunwind/test/unw_getcontext.pass.cpp create mode 100644 libunwind/test/unwind_leaffunction.pass.cpp diff --git a/libunwind/.clang-format b/libunwind/.clang-format new file mode 100644 index 0000000000..5bead5f39d --- /dev/null +++ b/libunwind/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM + diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt new file mode 100644 index 0000000000..9b55195b83 --- /dev/null +++ b/libunwind/CMakeLists.txt @@ -0,0 +1,373 @@ +if (NOT IS_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/../libcxx") + message(FATAL_ERROR "libunwind requires being built in a monorepo layout with libcxx available") +endif() + +#=============================================================================== +# 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/libunwind/LICENSE.TXT b/libunwind/LICENSE.TXT new file mode 100644 index 0000000000..1e3120621c --- /dev/null +++ b/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/libunwind/cmake/Modules/HandleCompilerRT.cmake b/libunwind/cmake/Modules/HandleCompilerRT.cmake new file mode 100644 index 0000000000..77168e5994 --- /dev/null +++ b/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/libunwind/cmake/Modules/HandleLibunwindFlags.cmake b/libunwind/cmake/Modules/HandleLibunwindFlags.cmake new file mode 100644 index 0000000000..675071f943 --- /dev/null +++ b/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/libunwind/cmake/config-ix.cmake b/libunwind/cmake/config-ix.cmake new file mode 100644 index 0000000000..4ca6bdd8e9 --- /dev/null +++ b/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/libunwind/docs/BuildingLibunwind.rst b/libunwind/docs/BuildingLibunwind.rst new file mode 100644 index 0000000000..9b11042a65 --- /dev/null +++ b/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/libunwind/docs/CMakeLists.txt b/libunwind/docs/CMakeLists.txt new file mode 100644 index 0000000000..79b87eb03b --- /dev/null +++ b/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/libunwind/docs/README.txt b/libunwind/docs/README.txt new file mode 100644 index 0000000000..968982fce5 --- /dev/null +++ b/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/libunwind/docs/conf.py b/libunwind/docs/conf.py new file mode 100644 index 0000000000..bc91d90fe8 --- /dev/null +++ b/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/libunwind/docs/index.rst b/libunwind/docs/index.rst new file mode 100644 index 0000000000..f7ff29d095 --- /dev/null +++ b/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/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h new file mode 100644 index 0000000000..a50ba05388 --- /dev/null +++ b/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/libunwind/include/libunwind.h b/libunwind/include/libunwind.h new file mode 100644 index 0000000000..5ba63e5b7e --- /dev/null +++ b/libunwind/include/libunwind.h @@ -0,0 +1,1176 @@ +//===---------------------------- 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__ + #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; + +#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/libunwind/include/mach-o/compact_unwind_encoding.h b/libunwind/include/mach-o/compact_unwind_encoding.h new file mode 100644 index 0000000000..5301b1055e --- /dev/null +++ b/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/libunwind/include/unwind.h b/libunwind/include/unwind.h new file mode 100644 index 0000000000..2101401337 --- /dev/null +++ b/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/libunwind/include/unwind_arm_ehabi.h b/libunwind/include/unwind_arm_ehabi.h new file mode 100644 index 0000000000..747129af5e --- /dev/null +++ b/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/libunwind/include/unwind_itanium.h b/libunwind/include/unwind_itanium.h new file mode 100644 index 0000000000..794e990dc0 --- /dev/null +++ b/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/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp new file mode 100644 index 0000000000..171318ff63 --- /dev/null +++ b/libunwind/src/AddressSpace.hpp @@ -0,0 +1,630 @@ +//===------------------------- 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__ + + 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; +} + +} // namespace libunwind + +#endif // __ADDRESSSPACE_HPP__ diff --git a/libunwind/src/CMakeLists.txt b/libunwind/src/CMakeLists.txt new file mode 100644 index 0000000000..ce3217fa80 --- /dev/null +++ b/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/libunwind/src/CompactUnwinder.hpp b/libunwind/src/CompactUnwinder.hpp new file mode 100644 index 0000000000..312bfbb2c7 --- /dev/null +++ b/libunwind/src/CompactUnwinder.hpp @@ -0,0 +1,697 @@ +//===-------------------------- 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 + 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 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::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) +/// 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 + 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) { + switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { + case UNWIND_ARM64_MODE_FRAME: + return stepWithCompactEncodingFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_ARM64_MODE_FRAMELESS: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +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(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(addressSpace.get64(fp + 8)); + + return UNW_STEP_SUCCESS; +} +#endif // _LIBUNWIND_TARGET_AARCH64 + + +} // namespace libunwind + +#endif // __COMPACT_UNWINDER_HPP__ diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp new file mode 100644 index 0000000000..b58c51bb7a --- /dev/null +++ b/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/libunwind/src/DwarfParser.hpp b/libunwind/src/DwarfParser.hpp new file mode 100644 index 0000000000..2a7155ba9e --- /dev/null +++ b/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/libunwind/src/EHHeaderParser.hpp b/libunwind/src/EHHeaderParser.hpp new file mode 100644 index 0000000000..f97cca5482 --- /dev/null +++ b/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/libunwind/src/FrameHeaderCache.hpp b/libunwind/src/FrameHeaderCache.hpp new file mode 100644 index 0000000000..54d5d33c3c --- /dev/null +++ b/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/libunwind/src/RWMutex.hpp b/libunwind/src/RWMutex.hpp new file mode 100644 index 0000000000..fcd3f4967d --- /dev/null +++ b/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/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp new file mode 100644 index 0000000000..5e2f11fbe1 --- /dev/null +++ b/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/libunwind/src/Unwind-EHABI.cpp b/libunwind/src/Unwind-EHABI.cpp new file mode 100644 index 0000000000..a564fd5240 --- /dev/null +++ b/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/libunwind/src/Unwind-EHABI.h b/libunwind/src/Unwind-EHABI.h new file mode 100644 index 0000000000..6897082a33 --- /dev/null +++ b/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/libunwind/src/Unwind-seh.cpp b/libunwind/src/Unwind-seh.cpp new file mode 100644 index 0000000000..ad0b267659 --- /dev/null +++ b/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/libunwind/src/Unwind-sjlj.c b/libunwind/src/Unwind-sjlj.c new file mode 100644 index 0000000000..fd2a95b74c --- /dev/null +++ b/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/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp new file mode 100644 index 0000000000..7157fa92bf --- /dev/null +++ b/libunwind/src/UnwindCursor.hpp @@ -0,0 +1,2146 @@ +//===------------------------- 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 +#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, 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 + +#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, void *) + : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { + memset(&_info, 0, sizeof(_info)); + // FIXME + // fill in _registers from thread arg +} + + +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/libunwind/src/UnwindLevel1-gcc-ext.c b/libunwind/src/UnwindLevel1-gcc-ext.c new file mode 100644 index 0000000000..4172540ef0 --- /dev/null +++ b/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/libunwind/src/UnwindLevel1.c b/libunwind/src/UnwindLevel1.c new file mode 100644 index 0000000000..9203ac771f --- /dev/null +++ b/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/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S new file mode 100644 index 0000000000..955ec3355f --- /dev/null +++ b/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/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S new file mode 100644 index 0000000000..e565c8ffcb --- /dev/null +++ b/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/libunwind/src/Unwind_AppleExtras.cpp b/libunwind/src/Unwind_AppleExtras.cpp new file mode 100644 index 0000000000..e3d41ca2b4 --- /dev/null +++ b/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/libunwind/src/assembly.h b/libunwind/src/assembly.h new file mode 100644 index 0000000000..e38d323369 --- /dev/null +++ b/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/libunwind/src/cet_unwind.h b/libunwind/src/cet_unwind.h new file mode 100644 index 0000000000..482e0c8086 --- /dev/null +++ b/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/libunwind/src/config.h b/libunwind/src/config.h new file mode 100644 index 0000000000..2ab9d2f5e0 --- /dev/null +++ b/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/libunwind/src/dwarf2.h b/libunwind/src/dwarf2.h new file mode 100644 index 0000000000..40f0daf468 --- /dev/null +++ b/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/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp new file mode 100644 index 0000000000..93e1bc131f --- /dev/null +++ b/libunwind/src/libunwind.cpp @@ -0,0 +1,341 @@ +//===--------------------------- 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) + +/// 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/libunwind/src/libunwind_ext.h b/libunwind/src/libunwind_ext.h new file mode 100644 index 0000000000..316dee2982 --- /dev/null +++ b/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/libunwind/test/CMakeLists.txt b/libunwind/test/CMakeLists.txt new file mode 100644 index 0000000000..932a6e3369 --- /dev/null +++ b/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/libunwind/test/alignment.compile.pass.cpp b/libunwind/test/alignment.compile.pass.cpp new file mode 100644 index 0000000000..4606dc5e53 --- /dev/null +++ b/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/libunwind/test/floatregister.pass.cpp b/libunwind/test/floatregister.pass.cpp new file mode 100644 index 0000000000..64107e6d49 --- /dev/null +++ b/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/libunwind/test/forceunwind.pass.cpp b/libunwind/test/forceunwind.pass.cpp new file mode 100644 index 0000000000..a3191f8f74 --- /dev/null +++ b/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/libunwind/test/frameheadercache_test.pass.cpp b/libunwind/test/frameheadercache_test.pass.cpp new file mode 100644 index 0000000000..dd1d3dd939 --- /dev/null +++ b/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/libunwind/test/libunwind/__init__.py b/libunwind/test/libunwind/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libunwind/test/libunwind/test/__init__.py b/libunwind/test/libunwind/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libunwind/test/libunwind/test/config.py b/libunwind/test/libunwind/test/config.py new file mode 100644 index 0000000000..87a810b49d --- /dev/null +++ b/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/libunwind/test/libunwind_01.pass.cpp b/libunwind/test/libunwind_01.pass.cpp new file mode 100644 index 0000000000..aceb8bedf8 --- /dev/null +++ b/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/libunwind/test/libunwind_02.pass.cpp b/libunwind/test/libunwind_02.pass.cpp new file mode 100644 index 0000000000..64d8d58831 --- /dev/null +++ b/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/libunwind/test/lit.cfg.py b/libunwind/test/lit.cfg.py new file mode 100644 index 0000000000..647464abe2 --- /dev/null +++ b/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/libunwind/test/lit.site.cfg.in b/libunwind/test/lit.site.cfg.in new file mode 100644 index 0000000000..4fd633f877 --- /dev/null +++ b/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/libunwind/test/remember_state_leak.pass.sh.s b/libunwind/test/remember_state_leak.pass.sh.s new file mode 100644 index 0000000000..fceac212c8 --- /dev/null +++ b/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/libunwind/test/signal_frame.pass.cpp b/libunwind/test/signal_frame.pass.cpp new file mode 100644 index 0000000000..fea189794b --- /dev/null +++ b/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/libunwind/test/signal_unwind.pass.cpp b/libunwind/test/signal_unwind.pass.cpp new file mode 100644 index 0000000000..5468c7f672 --- /dev/null +++ b/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/libunwind/test/unw_getcontext.pass.cpp b/libunwind/test/unw_getcontext.pass.cpp new file mode 100644 index 0000000000..a02c8e5403 --- /dev/null +++ b/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/libunwind/test/unwind_leaffunction.pass.cpp b/libunwind/test/unwind_leaffunction.pass.cpp new file mode 100644 index 0000000000..8fc343304c --- /dev/null +++ b/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 From d94d864b2a769b2ff361d687361361c041675195 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 18 Oct 2021 18:21:37 +0200 Subject: [PATCH 067/146] feat: Build vendored libunwind (NATIVE-274) (#53) This builds the vendored copy of libunwind on macOS when CRASHPAD_ENABLE_STACKTRACE is enabled. Co-authored-by: Sebastian Zivota --- CMakeLists.txt | 5 +++++ libunwind/CMakeLists.txt | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d7a479540..b7863dff9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,11 @@ 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) +endif() + if(CRASHPAD_ENABLE_INSTALL_DEV) install(EXPORT crashpad_export NAMESPACE crashpad:: FILE crashpad-targets.cmake DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt index 9b55195b83..ec0049ddb5 100644 --- a/libunwind/CMakeLists.txt +++ b/libunwind/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT IS_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}/../libcxx") - message(FATAL_ERROR "libunwind requires being built in a monorepo layout with libcxx available") -endif() - #=============================================================================== # Setup Project #=============================================================================== From 897bad95e7307908fc3aa744df9f504f8f564533 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Tue, 19 Oct 2021 12:36:06 +0200 Subject: [PATCH 068/146] feat: Do client-side stackwalking on Linux (NATIVE-152) (#49) Co-authored-by: Betty Da Co-authored-by: Arpad Borsos --- .github/workflows/build.yml | 2 +- snapshot/CMakeLists.txt | 6 +++ snapshot/linux/process_snapshot_linux.cc | 5 +++ snapshot/linux/thread_snapshot_linux.cc | 52 ++++++++++++++++++++++++ snapshot/linux/thread_snapshot_linux.h | 4 ++ 5 files changed, 68 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ce12943427..cbefe8feaa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,7 +22,7 @@ jobs: if: ${{ runner.os == 'Linux' }} run: | sudo apt update - sudo apt install zlib1g-dev libcurl4-openssl-dev libssl-dev libunwind-dev + sudo apt install zlib1g-dev libcurl4-openssl-dev libssl-dev libunwind-dev pkg-config - name: Build crashpad run: | diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index c684793993..8b3d9fd850 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -224,6 +224,12 @@ if(WIN32) endif() 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" diff --git a/snapshot/linux/process_snapshot_linux.cc b/snapshot/linux/process_snapshot_linux.cc index 35f870ec76..affa46cd0b 100644 --- a/snapshot/linux/process_snapshot_linux.cc +++ b/snapshot/linux/process_snapshot_linux.cc @@ -104,6 +104,11 @@ bool ProcessSnapshotLinux::InitializeException( 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)) { diff --git a/snapshot/linux/thread_snapshot_linux.cc b/snapshot/linux/thread_snapshot_linux.cc index e3e2bebddb..b9d889e757 100644 --- a/snapshot/linux/thread_snapshot_linux.cc +++ b/snapshot/linux/thread_snapshot_linux.cc @@ -16,6 +16,12 @@ #include +#ifdef CLIENT_STACKTRACES_ENABLED +#include +#include +#include +#endif + #include "base/logging.h" #include "snapshot/linux/cpu_context_linux.h" #include "util/misc/reinterpret_bytes.h" @@ -199,6 +205,37 @@ bool ThreadSnapshotLinux::Initialize(ProcessReaderLinux* process_reader, 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( @@ -243,5 +280,20 @@ std::vector ThreadSnapshotLinux::ExtraMemory() const { return std::vector(); } +#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/snapshot/linux/thread_snapshot_linux.h b/snapshot/linux/thread_snapshot_linux.h index 44cc6f6d97..0d84eeade5 100644 --- a/snapshot/linux/thread_snapshot_linux.h +++ b/snapshot/linux/thread_snapshot_linux.h @@ -57,6 +57,10 @@ class ThreadSnapshotLinux final : public ThreadSnapshot { 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) From b0677d2ae96a7943789c3c4850ef0f6af2778c2f Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 19 Oct 2021 12:37:01 +0200 Subject: [PATCH 069/146] feat: Add remote unwinding to libunwind (NATIVE-151) (#52) This patches our vendored libunwind version to support remote unwinding on macOS. More specifically, this revives some previously existing, but never finished or working code, which was removed in https://github.com/llvm/llvm-project/commit/368c02e3ec44e5418626f46abebcc22a69c7f66d --- libunwind/include/libunwind.h | 10 + libunwind/src/AddressSpace.hpp | 442 ++++++++++++++++++++++++++++++ libunwind/src/CompactUnwinder.hpp | 26 ++ libunwind/src/UnwindCursor.hpp | 31 ++- libunwind/src/libunwind.cpp | 37 +++ 5 files changed, 541 insertions(+), 5 deletions(-) diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h index 5ba63e5b7e..019fa9a767 100644 --- a/libunwind/include/libunwind.h +++ b/libunwind/include/libunwind.h @@ -19,6 +19,9 @@ #include #ifdef __APPLE__ + + #include + #if __clang__ #if __has_include() #include @@ -130,6 +133,13 @@ extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUN 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 diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 171318ff63..e856f64164 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -47,6 +47,12 @@ struct EHABIIndexEntry { #ifdef __APPLE__ + #include + #include + #include + #include + #include + struct dyld_unwind_sections { const struct mach_header* mh; @@ -625,6 +631,442 @@ inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf, 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/libunwind/src/CompactUnwinder.hpp b/libunwind/src/CompactUnwinder.hpp index 312bfbb2c7..369079ae60 100644 --- a/libunwind/src/CompactUnwinder.hpp +++ b/libunwind/src/CompactUnwinder.hpp @@ -274,6 +274,8 @@ class CompactUnwinder_x86_64 { 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, @@ -288,6 +290,8 @@ 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); @@ -301,6 +305,28 @@ int CompactUnwinder_x86_64::stepWithCompactEncoding( _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, diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 7157fa92bf..dd193856a8 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -23,6 +23,7 @@ #endif #ifdef __APPLE__ #include + #include #endif #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) @@ -889,7 +890,7 @@ class UnwindCursor : public AbstractUnwindCursor{ typedef typename A::pint_t pint_t; public: UnwindCursor(unw_context_t *context, A &as); - UnwindCursor(A &as, void *threadArg); + UnwindCursor(A &as, thread_t thread); virtual ~UnwindCursor() {} virtual bool validReg(int); virtual unw_word_t getReg(int); @@ -1226,13 +1227,31 @@ UnwindCursor::UnwindCursor(unw_context_t *context, A &as) } template -UnwindCursor::UnwindCursor(A &as, void *) +UnwindCursor::UnwindCursor(A &as, thread_t thread) : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { memset(&_info, 0, sizeof(_info)); - // FIXME - // fill in _registers from thread arg -} +#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) { @@ -1951,8 +1970,10 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { #endif // If unwind table has entry, but entry says there is no unwind info, // record that we have no unwind info. +#ifndef _LIBUNWIND_TARGET_X86_64 if (_info.format == 0) _unwindInfoMissing = true; +#endif return; } } diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index 93e1bc131f..fffffa76dd 100644 --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -88,6 +88,43 @@ _LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, } _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) { From e4719db8283fa1fe17047fe921422c2ab5319c1c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 19 Oct 2021 14:23:53 +0200 Subject: [PATCH 070/146] feat: Do client-side stackwalking on Mac (NATIVE-151) (#47) --- CMakeLists.txt | 1 + snapshot/CMakeLists.txt | 12 ++++++--- snapshot/mac/process_reader_mac.h | 3 ++- snapshot/mac/thread_snapshot_mac.cc | 39 ++++++++++++++++++++++++++--- 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b7863dff9d..de1a504ac6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,6 +124,7 @@ 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) diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index 8b3d9fd850..9d607e401a 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -46,7 +46,6 @@ add_library(crashpad_snapshot STATIC unloaded_module_snapshot.h ) - if(APPLE AND NOT IOS) target_sources(crashpad_snapshot PRIVATE posix/timezone.cc @@ -224,10 +223,15 @@ if(WIN32) 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) + 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") diff --git a/snapshot/mac/process_reader_mac.h b/snapshot/mac/process_reader_mac.h index 2f8ea9dcec..a462d1d144 100644 --- a/snapshot/mac/process_reader_mac.h +++ b/snapshot/mac/process_reader_mac.h @@ -181,6 +181,8 @@ class ProcessReaderMac { //! 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(). @@ -250,7 +252,6 @@ class ProcessReaderMac { std::vector modules_; std::vector> module_readers_; ProcessMemoryMac process_memory_; - task_t task_; // weak InitializationStateDcheck initialized_; #if defined(CRASHPAD_MAC_32_BIT_SUPPORT) diff --git a/snapshot/mac/thread_snapshot_mac.cc b/snapshot/mac/thread_snapshot_mac.cc index d261f194ad..7ae5f9fae4 100644 --- a/snapshot/mac/thread_snapshot_mac.cc +++ b/snapshot/mac/thread_snapshot_mac.cc @@ -18,6 +18,10 @@ #include "snapshot/mac/cpu_context_mac.h" #include "snapshot/mac/process_reader_mac.h" +#ifdef CLIENT_STACKTRACES_ENABLED +#include +#endif + namespace crashpad { namespace internal { @@ -31,11 +35,9 @@ ThreadSnapshotMac::ThreadSnapshotMac() thread_(MACH_PORT_NULL), suspend_count_(0), priority_(0), - initialized_() { -} + initialized_() {} -ThreadSnapshotMac::~ThreadSnapshotMac() { -} +ThreadSnapshotMac::~ThreadSnapshotMac() {} bool ThreadSnapshotMac::Initialize( ProcessReaderMac* process_reader, @@ -89,6 +91,35 @@ bool ThreadSnapshotMac::Initialize( #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; } From bc22a7b09ca881ab5fb2ada5720268d1e98f2857 Mon Sep 17 00:00:00 2001 From: Sebastian Zivota Date: Fri, 29 Oct 2021 13:04:08 +0200 Subject: [PATCH 071/146] feat: Clientside stacktraces for ARM64 [NATIVE-289] (#54) The changes that were needed to make this work: * strip the pointer-authentication code from instruction addresses we read do speculative link-register or frame-pointer based unwinding if we have a 0 compact encoding. Co-authored-by: Jan Michael Auer Co-authored-by: Arpad Borsos Co-authored-by: Arpad Borsos --- libunwind/src/CompactUnwinder.hpp | 76 ++++++++++++++++++++++++++++--- libunwind/src/UnwindCursor.hpp | 6 +-- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/libunwind/src/CompactUnwinder.hpp b/libunwind/src/CompactUnwinder.hpp index 369079ae60..bcf0424474 100644 --- a/libunwind/src/CompactUnwinder.hpp +++ b/libunwind/src/CompactUnwinder.hpp @@ -516,6 +516,13 @@ void CompactUnwinder_x86_64::framelessUnwind(A &addressSpace, #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 @@ -529,6 +536,8 @@ class CompactUnwinder_arm64 { 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, @@ -542,17 +551,72 @@ 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: - return stepWithCompactEncodingFrame(compactEncoding, functionStart, - addressSpace, registers); + result = stepWithCompactEncodingFrame(compactEncoding, functionStart, + addressSpace, registers); + registers.setRegister(UNW_AARCH64_LR, 0); + return result; case UNWIND_ARM64_MODE_FRAMELESS: - return stepWithCompactEncodingFrameless(compactEncoding, functionStart, - addressSpace, registers); + 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, @@ -630,7 +694,7 @@ int CompactUnwinder_arm64::stepWithCompactEncodingFrameless( registers.setSP(savedRegisterLoc); // set pc to be value in lr - registers.setIP(registers.getRegister(UNW_AARCH64_LR)); + registers.setIP(strip_ptr_auth(registers.getRegister(UNW_AARCH64_LR))); return UNW_STEP_SUCCESS; } @@ -711,7 +775,7 @@ int CompactUnwinder_arm64::stepWithCompactEncodingFrame( // old sp is fp less saved fp and lr registers.setSP(fp + 16); // pop return address into pc - registers.setIP(addressSpace.get64(fp + 8)); + registers.setIP(strip_ptr_auth(addressSpace.get64(fp + 8))); return UNW_STEP_SUCCESS; } diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index dd193856a8..f8dd3f272e 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -1970,10 +1970,8 @@ void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { #endif // If unwind table has entry, but entry says there is no unwind info, // record that we have no unwind info. -#ifndef _LIBUNWIND_TARGET_X86_64 - if (_info.format == 0) - _unwindInfoMissing = true; -#endif + // if (_info.format == 0) + // _unwindInfoMissing = true; return; } } From 23fe39510a3c2edc537d4b17805809be5dcf64df Mon Sep 17 00:00:00 2001 From: walterbrebels Date: Tue, 23 Nov 2021 11:42:48 +0100 Subject: [PATCH 072/146] build: Don't add /Zi to compiler flags with MSVC (#56) --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index de1a504ac6..e228933505 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -92,7 +92,6 @@ if(MSVC) $<$:/FS> $<$:/W4> $<$:/WX> - $<$:/Zi> $<$:/bigobj> # Support larger number of sections in obj file. $<$:/wd4100> # Unreferenced formal parameter. $<$:/wd4127> # Conditional expression is constant. From b9ff9fd3e237cf6ef930f01f1732b7d12c0316e6 Mon Sep 17 00:00:00 2001 From: Siim Meerits Date: Wed, 24 Nov 2021 10:17:05 +0200 Subject: [PATCH 073/146] msvc: Install getopt target and link crashpad handler library with getopt. (#55) The 'crashpad_handler' library uses getopt functionality in 'HandlerMain' function. Hence when implementing your own crash handler instead of using bundled handler program the getopt library needs to be linked in as well. --- handler/CMakeLists.txt | 1 + third_party/getopt/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 75f8bbb41e..86dcdff9a3 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -64,6 +64,7 @@ target_link_libraries(crashpad_handler_lib if(WIN32) if(MSVC) + target_link_libraries(crashpad_handler_lib PUBLIC crashpad_getopt) target_compile_options(crashpad_handler_lib PRIVATE "/wd4201") endif() endif() diff --git a/third_party/getopt/CMakeLists.txt b/third_party/getopt/CMakeLists.txt index 91dd2ff56e..24175ce593 100644 --- a/third_party/getopt/CMakeLists.txt +++ b/third_party/getopt/CMakeLists.txt @@ -9,6 +9,7 @@ if(MSVC) target_link_libraries(crashpad_getopt PRIVATE $ ) + crashpad_install_target(crashpad_getopt) else() add_library(crashpad_getopt INTERFACE) endif() From e16aa44de092673755f3c225f55997b07a7984c7 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 29 Nov 2021 13:32:48 +0100 Subject: [PATCH 074/146] fix: Pass safeseh flag to MSVC asm compiler (#57) --- util/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 28218b856e..d3d3848d78 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -474,6 +474,9 @@ if(WIN32) target_link_libraries(crashpad_util PRIVATE user32 version winhttp) if(MSVC) target_compile_options(crashpad_util PRIVATE "/wd4201") + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(CMAKE_ASM_MASM_FLAGS "${CMAKE_ASM_MASM_FLAGS} /safeseh") + endif() elseif(MINGW) target_compile_options(crashpad_util PRIVATE $<$:-municode> From a545377d07ff38c06cec05de8026a18955b46c57 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 29 Nov 2021 17:44:53 +0100 Subject: [PATCH 075/146] fix: Use correct ASM files on Windows ARM64 (#58) --- third_party/zlib/CMakeLists.txt | 2 +- util/CMakeLists.txt | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index f912af5ed6..eeb449ff33 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -35,6 +35,7 @@ else() zlib/trees.c zlib/trees.h zlib/uncompr.c + zlib/x86.h zlib/zconf.h zlib/zlib.h zlib/zutil.c @@ -52,7 +53,6 @@ else() zlib/crc_folding.c zlib/fill_window_sse.c zlib/x86.c - zlib/x86.h ) if(NOT MSVC) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index d3d3848d78..eba7b72e8a 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -339,9 +339,17 @@ if(WIN32) win/termination_codes.h win/traits.h win/xp_compat.h - misc/capture_context_win.asm - win/safe_terminate_process.asm ) + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + target_sources(crashpad_util PRIVATE + misc/capture_context_win_arm64.asm + ) + else() + target_sources(crashpad_util PRIVATE + misc/capture_context_win.asm + win/safe_terminate_process.asm + ) + endif() endif() # Copied from: https://github.com/qedsoftware/crashpad/blob/3583c50a6575857abcf140f6ea3b8d11390205b3/util/CMakeLists.txt#L196-L233 From 2ab34ff319a02ae356f2d51e38128306cfca797b Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 3 Dec 2021 15:12:34 +0100 Subject: [PATCH 076/146] Sync CMake files --- client/CMakeLists.txt | 4 ++++ snapshot/CMakeLists.txt | 2 ++ util/CMakeLists.txt | 4 +++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 5357c8624f..4b35a0f187 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -32,8 +32,12 @@ if(IOS) 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() diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index 9d607e401a..f83f4b417d 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -111,6 +111,8 @@ 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 diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index eba7b72e8a..2b42bd9f51 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -167,6 +167,7 @@ if(APPLE) misc/capture_context_mac.S misc/clock_mac.cc misc/paths_mac.cc + net/http_transport_mac.mm synchronization/semaphore_mac.cc ) if(NOT IOS) @@ -195,7 +196,6 @@ if(APPLE) mach/scoped_task_suspend.h mach/task_for_pid.cc mach/task_for_pid.h - net/http_transport_mac.mm posix/process_info_mac.cc process/process_memory_mac.cc process/process_memory_mac.h @@ -205,6 +205,8 @@ if(APPLE) ios/ios_intermediate_dump_data.cc ios/ios_intermediate_dump_data.h ios/ios_intermediate_dump_format.h + ios/ios_intermediate_dump_interface.cc + ios/ios_intermediate_dump_interface.h ios/ios_intermediate_dump_list.cc ios/ios_intermediate_dump_list.h ios/ios_intermediate_dump_map.cc From 96874a17fb5f32680a7ecccd74c70de0596f871d Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 3 Dec 2021 15:15:14 +0100 Subject: [PATCH 077/146] Sync mini_chromium --- third_party/mini_chromium/CMakeLists.txt | 2 +- third_party/mini_chromium/mini_chromium | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 216f3d477f..342b7aa3d9 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -24,9 +24,9 @@ mc_append_sources( files/scoped_file.cc files/scoped_file.h format_macros.h + ignore_result.h logging.cc logging.h - macros.h memory/free_deleter.h memory/page_size.h memory/scoped_policy.h diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 8f7a60f2c6..0e22eed71e 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 8f7a60f2c637f2a3c5d25f320739b3de7c2e325d +Subproject commit 0e22eed71eec97dacbe80822a14c5cd0b580d793 From 6c6bdbece6daa17d10330eae202efbf8400b8193 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 3 Dec 2021 15:36:23 +0100 Subject: [PATCH 078/146] Manually disallow copy construction --- minidump/minidump_stacktrace_writer.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/minidump/minidump_stacktrace_writer.h b/minidump/minidump_stacktrace_writer.h index cddea4e7db..2ba5ae901e 100644 --- a/minidump/minidump_stacktrace_writer.h +++ b/minidump/minidump_stacktrace_writer.h @@ -8,7 +8,6 @@ #include #include -#include "base/macros.h" #include "minidump/minidump_extensions.h" #include "minidump/minidump_stream_writer.h" #include "minidump/minidump_thread_id_map.h" @@ -48,6 +47,11 @@ class MinidumpStacktraceListWriter final : public internal::MinidumpStreamWriter { public: MinidumpStacktraceListWriter(); + + MinidumpStacktraceListWriter(const MinidumpStacktraceListWriter&) = delete; + MinidumpStacktraceListWriter& operator=(const MinidumpStacktraceListWriter&) = + delete; + ~MinidumpStacktraceListWriter() override; //! \brief TODO @@ -77,8 +81,6 @@ class MinidumpStacktraceListWriter final std::vector frames_; std::vector symbol_bytes_; internal::Header stacktrace_header_; - - DISALLOW_COPY_AND_ASSIGN(MinidumpStacktraceListWriter); }; } // namespace crashpad From a32cbe1f9a18bcfccc3952425419059eadd3d95c Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Fri, 3 Dec 2021 16:10:05 +0100 Subject: [PATCH 079/146] Simplify Attachment Support diff --- client/crash_report_database_mac.mm | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/client/crash_report_database_mac.mm b/client/crash_report_database_mac.mm index 8240767d41..d1c0a3ed2e 100644 --- a/client/crash_report_database_mac.mm +++ b/client/crash_report_database_mac.mm @@ -49,7 +49,6 @@ constexpr char kWriteDirectory[] = "new"; constexpr char kUploadPendingDirectory[] = "pending"; constexpr char kCompletedDirectory[] = "completed"; -constexpr char kAttachmentsDirectory[] = "attachments"; constexpr char kSettings[] = "settings.dat"; @@ -72,14 +71,6 @@ constexpr char kXattrDatabaseInitialized[] = "initialized"; -bool AttachmentNameIsOK(const std::string& name) { - for (const char c : name) { - if (c != '_' && c != '-' && c != '.' && !isalnum(c)) - return false; - } - return true; -} - // 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. @@ -471,6 +462,7 @@ OperationStatus ReportsInDirectory(const base::FilePath& path, return kFileSystemError; } + upload_report->database_ = this; upload_report->lock_fd.reset(lock.release()); upload_report->report_metrics_ = report_metrics; report->reset(upload_report.release()); From c8c30a602e0eb2c22d6eb104067c8fdb7e0557e3 Mon Sep 17 00:00:00 2001 From: Walter Brebels Date: Thu, 7 Apr 2022 13:43:46 +0200 Subject: [PATCH 080/146] Find python3 or python2 --- util/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 2b42bd9f51..5ae9b3521a 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -376,7 +376,7 @@ if(APPLE) list(APPEND input_files "${full_path}") endforeach() - find_package(PythonInterp 2.7 REQUIRED) + find_package(Python COMPONENTS Interpreter REQUIRED) set(output_dir "${CMAKE_CURRENT_BINARY_DIR}/util/mach") file(MAKE_DIRECTORY "${output_dir}") @@ -428,7 +428,7 @@ if(APPLE) OUTPUT ${output_files} COMMAND - "${PYTHON_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" ${archs} ${sdk} ${includes} "${input}" ${output_files} + "${Python_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" ${archs} ${sdk} ${includes} "${input}" ${output_files} DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/mach/mig.py" "${input}" ) From 12b09609eb8681b8344c7193740b4772b3f932fd Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 12 Apr 2022 11:26:16 +0200 Subject: [PATCH 081/146] fix: Make sure to free global Lock on shutdown (#61) --- client/crashpad_client_win.cc | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 57696054de..6934831af0 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -69,7 +69,18 @@ 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; +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. @@ -394,8 +405,7 @@ bool StartHandlerProcess( } for (const base::FilePath& attachment : data->attachments) { AppendCommandLineArgument( - FormatArgumentString("attachment", attachment.value()), - &command_line); + FormatArgumentString("attachment", attachment.value()), &command_line); } ScopedKernelHANDLE this_process( From 412e849b4f1d8bc26165ce289b2a72da88609ec6 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 12 Apr 2022 11:47:25 +0200 Subject: [PATCH 082/146] Sync submodules and build files --- third_party/lss/lss | 2 +- third_party/mini_chromium/CMakeLists.txt | 1 - third_party/mini_chromium/mini_chromium | 2 +- util/CMakeLists.txt | 3 +++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/third_party/lss/lss b/third_party/lss/lss index 7bde79cc27..e1e7b0ad8e 160000 --- a/third_party/lss/lss +++ b/third_party/lss/lss @@ -1 +1 @@ -Subproject commit 7bde79cc274d06451bf65ae82c012a5d3e476b5a +Subproject commit e1e7b0ad8ee99a875b272c8e33e308472e897660 diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 342b7aa3d9..4577c1d125 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -24,7 +24,6 @@ mc_append_sources( files/scoped_file.cc files/scoped_file.h format_macros.h - ignore_result.h logging.cc logging.h memory/free_deleter.h diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 0e22eed71e..5654edb422 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 0e22eed71eec97dacbe80822a14c5cd0b580d793 +Subproject commit 5654edb4225bcad13901155c819febb5748e502b diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 5ae9b3521a..e21bbcb099 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -221,6 +221,8 @@ if(APPLE) ios/ios_system_data_collector.mm ios/raw_logging.cc ios/raw_logging.h + ios/scoped_background_task.h + ios/scoped_background_task.mm ios/scoped_vm_read.cc ios/scoped_vm_read.h ) @@ -303,6 +305,7 @@ if(WIN32) win/context_wrappers.h win/critical_section_with_debug_info.cc win/critical_section_with_debug_info.h + win/exception_codes.h win/exception_handler_server.cc win/exception_handler_server.h win/get_function.cc From ed59c4260f5771c55118ae45589db7452732cf12 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 12 Apr 2022 12:23:25 +0200 Subject: [PATCH 083/146] Raise C++ std to C++17, and fix chromeos build flags --- CMakeLists.txt | 2 +- third_party/mini_chromium/build/chromeos_buildflags.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e228933505..b13b78faea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,7 @@ else() enable_language(ASM) endif() -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) add_library(crashpad_interface INTERFACE) diff --git a/third_party/mini_chromium/build/chromeos_buildflags.h b/third_party/mini_chromium/build/chromeos_buildflags.h index c54f076845..72714a24ac 100644 --- a/third_party/mini_chromium/build/chromeos_buildflags.h +++ b/third_party/mini_chromium/build/chromeos_buildflags.h @@ -7,7 +7,7 @@ #include "build/buildflag.h" -#define BUILDFLAG_INTERNAL_IS_CHROMEOS_LACROS() (0) -#define BUILDFLAG_INTERNAL_IS_CHROMEOS_ASH() (0) +#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_ From c372875a79a189d8856dc255c339f4b890b83b00 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 12 Apr 2022 12:38:16 +0200 Subject: [PATCH 084/146] Enable ARC for the iOS build --- util/CMakeLists.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index e21bbcb099..e37bfb155e 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -225,7 +225,15 @@ if(APPLE) ios/scoped_background_task.mm ios/scoped_vm_read.cc ios/scoped_vm_read.h - ) + ) + # This specific file requires ARC support, while other parts do not + # build when ARC is enabled. + set_source_files_properties( + ios/scoped_background_task.mm + PROPERTIES + COMPILE_FLAGS + "-fobjc-arc" + ) endif() endif() From df2cb4c93bb512650f2bff43f69ae2bf18d929d3 Mon Sep 17 00:00:00 2001 From: Andrei Malashkin Date: Thu, 12 May 2022 05:09:25 -0700 Subject: [PATCH 085/146] CMake: try to use jwasm for mingw build, if possible (#63) --- CMakeLists.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b13b78faea..b6e2e6d5ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,22 @@ endfunction() if(WIN32) enable_language(ASM_MASM) + if(MINGW) + find_program(JWASM_FOUND jwasm) + if (JWASM_FOUND) + set(CMAKE_ASM_MASM_COMPILER ${JWASM_FOUND}) + execute_process(COMMAND ${CMAKE_C_COMPILER} --version OUTPUT_VARIABLE COMPILER_VERSION_OUTPUT) + if (COMPILER_VERSION_OUTPUT) + if (COMPILER_VERSION_OUTPUT MATCHES "x86_64") + set(JWASM_FLAGS -win64) + else() + set(JWASM_FLAGS -coff) + endif() + endif() + set(CMAKE_ASM_MASM_FLAGS ${CMAKE_ASM_MASM_FLAGS} ${JWASM_FLAGS}) + endif(JWASM_FOUND) + 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") @@ -60,7 +75,7 @@ if(WIN32) if(NOT CMAKE_ASM_MASM_FLAGS) set(CMAKE_ASM_MASM_FLAGS "-win64 -10") #use default compatibility flags endif() - endif() + endif(MINGW) else() enable_language(ASM) endif() From 21d03eec9750c67f769ab7c14fcedc9dd9c38d01 Mon Sep 17 00:00:00 2001 From: Anton Siluanov Date: Wed, 1 Jun 2022 10:42:13 +0300 Subject: [PATCH 086/146] Fix attachments name instead of ignoring with error (#67) Co-authored-by: Anton Syluanov --- client/crash_report_database.cc | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/client/crash_report_database.cc b/client/crash_report_database.cc index 7a3732b8d8..f3e3d53f91 100644 --- a/client/crash_report_database.cc +++ b/client/crash_report_database.cc @@ -28,12 +28,13 @@ 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; +std::string FixAttachmentName(std::string name) { + std::replace_if(name.begin(), name.end(), [&](char c) + { + return c != '_' && c != '-' && c != '.' && !isalnum(c); + }, '_'); + + return name; } } // namespace @@ -94,19 +95,15 @@ FileReaderInterface* CrashReportDatabase::NewReport::Reader() { 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); + const std::wstring name_string = base::UTF8ToWide(FixAttachmentName(name)); #else - const std::string name_string = name; + const std::string name_string = FixAttachmentName(name); #endif base::FilePath attachment_path = report_attachments_dir.Append(name_string); auto writer = std::make_unique(); From 99a5c423e76255e2e1fabcc50dc2e6148785bcde Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Wed, 15 Jun 2022 12:10:08 +0200 Subject: [PATCH 087/146] Fix windows client-stacktrace build by... switching to the new thread-context getter instead of the previously exposed union. For context: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3538668 --- snapshot/win/process_reader_win.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index cbcc6ba4a0..8704d3d733 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -153,7 +153,7 @@ void DoStackWalk(ProcessReaderWin::Thread* thread, int machine_type = IMAGE_FILE_MACHINE_I386; LPVOID ctx = NULL; #if defined(ARCH_CPU_X86) - const CONTEXT* ctx_ = &thread->context.native; + const CONTEXT* ctx_ = thread->context.context(); stack_frame.AddrPC.Offset = ctx_->Eip; stack_frame.AddrFrame.Offset = ctx_->Ebp; stack_frame.AddrStack.Offset = ctx_->Esp; @@ -162,7 +162,7 @@ void DoStackWalk(ProcessReaderWin::Thread* thread, // if (!is_64_reading_32) { machine_type = IMAGE_FILE_MACHINE_AMD64; - const CONTEXT* ctx_ = &thread->context.native; + const CONTEXT* ctx_ = thread->context.context(); stack_frame.AddrPC.Offset = ctx_->Rip; stack_frame.AddrFrame.Offset = ctx_->Rbp; stack_frame.AddrStack.Offset = ctx_->Rsp; From 86a1fa58c13d5d75b5191728fee710ef2ffc7716 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Wed, 15 Jun 2022 15:06:42 +0200 Subject: [PATCH 088/146] Fixing linux build by explicitly copying the thread_id from a packed... ...struct before "emplacing" it into the thread_names map in ProcessSnapshotMinidump::InitializeThreadNames(). --- snapshot/minidump/process_snapshot_minidump.cc | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/snapshot/minidump/process_snapshot_minidump.cc b/snapshot/minidump/process_snapshot_minidump.cc index 28942447dc..4017c91bce 100644 --- a/snapshot/minidump/process_snapshot_minidump.cc +++ b/snapshot/minidump/process_snapshot_minidump.cc @@ -643,7 +643,19 @@ bool ProcessSnapshotMinidump::InitializeThreadNames() { return false; } - thread_names_.emplace(minidump_thread_name.ThreadId, std::move(name)); + // XXX sentry maintainers: + // the upstream line + // + // thread_names_.emplace(minidump_thread_name.ThreadId, std::move(name)); + // + // fails to compile on GCC (which is untested/-supported by the crashpad + // maintainers). emplace() takes its parameters as rvalue-references + // which is illegal when referencing a bitfield (or packed struct). + // + // Creating an explicit copy by-passes the issue, trading for more + // (typically two) instructions per thread-name. + uint32_t thread_id = minidump_thread_name.ThreadId; + thread_names_.emplace(thread_id, std::move(name)); } return true; From c25a4a77588e81d3c4ac09f89edeed587fa30a9a Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Mon, 18 Jul 2022 13:04:25 +0200 Subject: [PATCH 089/146] update submodules and add WER handler to CMake --- handler/CMakeLists.txt | 24 ++++++++++++++++++++++++ third_party/mini_chromium/mini_chromium | 2 +- util/CMakeLists.txt | 4 ++-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 86dcdff9a3..fde7d26f64 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -122,3 +122,27 @@ if(NOT IOS) RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) endif() + +if(WIN32) + add_library(crashpad_wer SHARED + win/wer/crashpad_wer.cc + win/wer/crashpad_wer.h + win/wer/crashpad_wer.def + win/wer/crashpad_wer_main.cc + ../util/misc/address_types.h + ../util/win/address_types.h + ../util/win/registration_protocol_win.h + ) + + target_link_libraries(crashpad_wer + PRIVATE + $ + ) + + set_property(TARGET crashpad_wer PROPERTY EXPORT_NAME crashpad_wer) + add_library(crashpad::wer ALIAS crashpad_wer) + + install(TARGETS crashpad_wer EXPORT crashpad_export + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) +endif() diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 5654edb422..75dcb8dc41 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 5654edb4225bcad13901155c819febb5748e502b +Subproject commit 75dcb8dc417af77fdb9ec23c7b51cb1d57dfcee2 diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index e37bfb155e..8a72636ff9 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -132,11 +132,11 @@ if(NOT WIN32) thread/thread_posix.cc posix/close_multiple.cc posix/close_multiple.h - posix/double_fork_and_exec.cc - posix/double_fork_and_exec.h posix/drop_privileges.cc posix/drop_privileges.h posix/process_info.h + posix/spawn_subprocess.cc + posix/spawn_subprocess.h posix/symbolic_constants_posix.cc posix/symbolic_constants_posix.h ) From d81d0deefcb1abee0074ebcbd3e98650704c4593 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 2 Aug 2022 19:43:00 +0200 Subject: [PATCH 090/146] Expose enabled-flag for WER --- CMakeLists.txt | 9 +++++++++ handler/CMakeLists.txt | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b6e2e6d5ac..ffdc9bb1f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -115,6 +115,15 @@ if(MSVC) $<$:/wd4577> # 'noexcept' used with no exception handling mode specified. $<$:/wd4996> # 'X' was declared deprecated. ) + + # WER support is only available starting from Win10 build 10941 + if(${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} VERSION_LESS 10.0.19041) + message(STATUS "WER support disabled. Needs target platform >= 10.0.19041 (actual: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})") + else() + SET(CRASHPAD_WER_ENABLED TRUE) + SET(CRASHPAD_WER_ENABLED TRUE PARENT_SCOPE) + message(STATUS "WER support enabled") + endif() elseif(MINGW) # redirect to wmain # FIXME: cmake 3.13 added target_link_options diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index fde7d26f64..8fe882ac89 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -123,7 +123,7 @@ if(NOT IOS) ) endif() -if(WIN32) +if(CRASHPAD_WER_ENABLED) add_library(crashpad_wer SHARED win/wer/crashpad_wer.cc win/wer/crashpad_wer.h From 7a1f241bd5ff62b57f8f4a203101030d707e70b7 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 13 Sep 2022 16:54:51 +0200 Subject: [PATCH 091/146] Update third-party and CMakeLists.txt --- third_party/mini_chromium/mini_chromium | 2 +- util/CMakeLists.txt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 75dcb8dc41..4332ddb696 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 75dcb8dc417af77fdb9ec23c7b51cb1d57dfcee2 +Subproject commit 4332ddb6963750e1106efdcece6d6e2de6dc6430 diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 8a72636ff9..4c1d0e6f93 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -337,6 +337,7 @@ if(WIN32) win/process_structs.h win/registration_protocol_win.cc win/registration_protocol_win.h + win/registration_protocol_win_structs.h win/safe_terminate_process.h win/scoped_handle.cc win/scoped_handle.h From 8255e5f275f3986428f7a604dcbebfa23e37f917 Mon Sep 17 00:00:00 2001 From: Vitalii Koshura Date: Mon, 17 Oct 2022 15:26:52 +0200 Subject: [PATCH 092/146] Fix build when CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION is undefined (#73) Signed-off-by: Vitalii Koshura --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ffdc9bb1f0..f418e4af54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,7 @@ if(MSVC) ) # WER support is only available starting from Win10 build 10941 - if(${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION} VERSION_LESS 10.0.19041) + if("${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" VERSION_LESS 10.0.19041) message(STATUS "WER support disabled. Needs target platform >= 10.0.19041 (actual: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})") else() SET(CRASHPAD_WER_ENABLED TRUE) From 1f23bde898843e4517861639213b2949f136263a Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 12 Dec 2022 22:16:50 +0100 Subject: [PATCH 093/146] set CRASHPAD_FLOCK_ALWAYS_SUPPORTED in build... ...to prevent a failing windows build. The windows build failed because we're compiling a section in `client/settings.cc` that should be conditionally excluded by the pre-processor on all systems besides ANDROID and FUCHSIA. Adding compile-defs equivalent to: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/3999273/7/build/crashpad_buildconfig.gni --- CMakeLists.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index f418e4af54..f83ee5903d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ if(CRASHPAD_ZLIB_SYSTEM) find_package(ZLIB REQUIRED) endif() + if(LINUX OR ANDROID) find_package(OpenSSL) if(OPENSSL_FOUND) @@ -32,6 +33,12 @@ if(LINUX OR ANDROID) endif() endif() +if (NOT (ANDROID OR FUCHSIA)) + add_compile_definitions(CRASHPAD_FLOCK_ALWAYS_SUPPORTED=1) +else() + add_compile_definitions(CRASHPAD_FLOCK_ALWAYS_SUPPORTED=0) +endif() + include(GNUInstallDirs) set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/crashpad") From 4edb2bb2544bb0f9f8d5b28d2cb86ee5c6663e3d Mon Sep 17 00:00:00 2001 From: Bogdan Livadariu Date: Tue, 17 Jan 2023 15:52:37 +0200 Subject: [PATCH 094/146] fix: upload crashes on linux (#75) Co-authored-by: Mischan Toosarani-Hausberger --- util/CMakeLists.txt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 4c1d0e6f93..9789ce58b0 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -245,8 +245,17 @@ if(ANDROID) endif() if(LINUX OR ANDROID) + if (LINUX) + find_package(CURL REQUIRED) + target_include_directories(crashpad_util PRIVATE ${CURL_INCLUDE_DIRS}) + target_link_libraries(crashpad_util PRIVATE ${CURL_LIBRARIES}) + SET(HTTP_TRANSPORT_IMPL net/http_transport_libcurl.cc) + else() + SET(HTTP_TRANSPORT_IMPL net/http_transport_socket.cc) + endif() + target_sources(crashpad_util PRIVATE - net/http_transport_socket.cc + ${HTTP_TRANSPORT_IMPL} linux/address_types.h linux/auxiliary_vector.cc linux/auxiliary_vector.h From 918fd319d679306c8c95ee92376c6fa6ef3407a0 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Tue, 17 Jan 2023 14:53:29 +0100 Subject: [PATCH 095/146] fix: Avoid accidentally mutating `CONTEXT` when stack walking (#77) --- snapshot/win/process_reader_win.cc | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index b9718beb88..7573a73509 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -151,22 +151,20 @@ void DoStackWalk(ProcessReaderWin::Thread* thread, stack_frame.AddrStack.Mode = AddrModeFlat; int machine_type = IMAGE_FILE_MACHINE_I386; - LPVOID ctx = NULL; + CONTEXT ctx; #if defined(ARCH_CPU_X86) - const CONTEXT* ctx_ = thread->context.context(); - stack_frame.AddrPC.Offset = ctx_->Eip; - stack_frame.AddrFrame.Offset = ctx_->Ebp; - stack_frame.AddrStack.Offset = ctx_->Esp; - ctx = (LPVOID)ctx_; + ctx = *thread->context.context(); + stack_frame.AddrPC.Offset = ctx.Eip; + stack_frame.AddrFrame.Offset = ctx.Ebp; + stack_frame.AddrStack.Offset = ctx.Esp; #elif defined(ARCH_CPU_X86_64) // if (!is_64_reading_32) { machine_type = IMAGE_FILE_MACHINE_AMD64; - const CONTEXT* ctx_ = thread->context.context(); - stack_frame.AddrPC.Offset = ctx_->Rip; - stack_frame.AddrFrame.Offset = ctx_->Rbp; - stack_frame.AddrStack.Offset = ctx_->Rsp; - ctx = (LPVOID)ctx_; + ctx = *thread->context.context(); + stack_frame.AddrPC.Offset = ctx.Rip; + stack_frame.AddrFrame.Offset = ctx.Rbp; + stack_frame.AddrStack.Offset = ctx.Rsp; // } else { // const WOW64_CONTEXT* ctx_ = &thread->context.wow64; // stack_frame.AddrPC.Offset = ctx_->Eip; @@ -176,7 +174,7 @@ void DoStackWalk(ProcessReaderWin::Thread* thread, // } // TODO: we dont support this right away, maybe in the future -//#elif defined(ARCH_CPU_ARM64) +// #elif defined(ARCH_CPU_ARM64) // machine_type = IMAGE_FILE_MACHINE_ARM64; #else #error Unsupported Windows Arch @@ -192,7 +190,7 @@ void DoStackWalk(ProcessReaderWin::Thread* thread, process, thread_handle, &stack_frame, - ctx, + &ctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, From a0ab0e24d9c1d380751e8bba4e48efa5cd45d5cc Mon Sep 17 00:00:00 2001 From: pastdue <30942300+past-due@users.noreply.github.com> Date: Wed, 25 Jan 2023 17:52:28 -0500 Subject: [PATCH 096/146] mingw: Add missing MINIDUMP_THREAD_NAME / MINIDUMP_THREAD_NAME_LIST definitions --- compat/mingw/dbghelp.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/compat/mingw/dbghelp.h b/compat/mingw/dbghelp.h index b492198485..bfaf5113d7 100644 --- a/compat/mingw/dbghelp.h +++ b/compat/mingw/dbghelp.h @@ -329,6 +329,36 @@ typedef struct MINIDUMP_MISC_INFO_5 MINIDUMP_MISC_INFO_N; #endif +#ifdef __cplusplus +extern "C" { +#endif + +//! \brief Contains the name of the thread with the given thread ID. +struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_NAME { + //! \brief The identifier of the thread. + uint32_t ThreadId; + + //! \brief RVA64 of a MINIDUMP_STRING containing the name of the thread. + RVA64 RvaOfThreadName; +}; + +//! \brief Variable-sized struct which contains a list of MINIDUMP_THREAD_NAME +//! structs. +struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_NAME_LIST { + //! \brief The number of MINIDUMP_THREAD_NAME structs following this field. + uint32_t NumberOfThreadNames; + + //! \brief A variably-sized array containing zero of more + //! MINIDUMP_THREAD_NAME. + //! The length of the array is indicated by the NumberOfThreadNames field + //! in this struct. + struct MINIDUMP_THREAD_NAME ThreadNames[0]; +}; + +#ifdef __cplusplus +} +#endif + #pragma clang diagnostic pop #endif // CRASHPAD_COMPAT_MINGW_DBGHELP_H_ From 3dc58f40c8553cd8b990618463c0c23941f3ebec Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:55:42 -0500 Subject: [PATCH 097/146] mingw: Add missing GetThreadDescription definition --- compat/mingw/winnt.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/compat/mingw/winnt.h b/compat/mingw/winnt.h index 268af1b3bc..44216d7851 100644 --- a/compat/mingw/winnt.h +++ b/compat/mingw/winnt.h @@ -57,4 +57,16 @@ struct PROCESSOR_POWER_INFORMATION { #define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13 #endif +#include + +#ifdef __cplusplus +extern "C" { +#endif + +WINBASEAPI HRESULT WINAPI GetThreadDescription(HANDLE,PWSTR *); + +#ifdef __cplusplus +} +#endif + #endif // CRASHPAD_COMPAT_MINGW_WINNT_H_ From cc4a6ae51e19c8825e74e9fc4fcb9dc25143546a Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:58:12 -0500 Subject: [PATCH 098/146] win32: Ensure InitializeContext2 definition availability On both mingw and MSVC (when targeting < Windows 10) --- snapshot/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index f83f4b417d..c9488173d5 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -223,6 +223,15 @@ if(WIN32) "-Wno-unknown-attributes" ) endif() + + # Define NTDDI_VERSION >= NTDDI_WIN10_RS5 - so InitializeContext2 definition is always available in process_reader_win.cc + # NTDDI_WIN10_RS5 = 0x0A000006 /* ABRACADABRA_WIN10_RS5 */ + set_property( + SOURCE "win/process_reader_win.cc" + APPEND + PROPERTY COMPILE_DEFINITIONS + WINVER=0x0A00 _WIN32_WINNT=0x0A00 NTDDI_VERSION=0x0A000006 + ) endif() if(APPLE AND NOT IOS AND CRASHPAD_ENABLE_STACKTRACE) From b66ab3f80e491f855b3a8578a6c1794398dfbd68 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Thu, 26 Jan 2023 15:59:44 -0500 Subject: [PATCH 099/146] mingw: WER support --- CMakeLists.txt | 5 +++++ compat/mingw/werapi.h | 28 ++++++++++++++++++++++++++++ handler/CMakeLists.txt | 3 +++ 3 files changed, 36 insertions(+) create mode 100644 compat/mingw/werapi.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f83ee5903d..624aaaee26 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,6 +135,11 @@ elseif(MINGW) # redirect to wmain # FIXME: cmake 3.13 added target_link_options set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") + if(CRASHPAD_WER_ENABLED) + message(STATUS "WER support enabled") + else() + message(STATUS "WER support disabled. Define CRASHPAD_WER_ENABLED = TRUE to enable.") + endif() endif() add_library(crashpad::interface ALIAS crashpad_interface) diff --git a/compat/mingw/werapi.h b/compat/mingw/werapi.h new file mode 100644 index 0000000000..2007a05384 --- /dev/null +++ b/compat/mingw/werapi.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_MINGW_WERAPI_H_ +#define CRASHPAD_COMPAT_MINGW_WERAPI_H_ + +typedef HANDLE HREPORT; + +#ifndef WER_MAX_PREFERRED_MODULES_BUFFER +#define WER_MAX_PREFERRED_MODULES_BUFFER 256 +#endif + +#define PWER_SUBMIT_RESULT WER_SUBMIT_RESULT* + +#include_next + +#endif // CRASHPAD_COMPAT_MINGW_WERAPI_H_ diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 8fe882ac89..8c7a620435 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -137,9 +137,12 @@ if(CRASHPAD_WER_ENABLED) target_link_libraries(crashpad_wer PRIVATE $ + PUBLIC + crashpad_compat ) set_property(TARGET crashpad_wer PROPERTY EXPORT_NAME crashpad_wer) + set_property(TARGET crashpad_wer PROPERTY PREFIX "") # ensure MINGW doesn't prefix "lib" to dll name add_library(crashpad::wer ALIAS crashpad_wer) install(TARGETS crashpad_wer EXPORT crashpad_export From 817cb6563c9f95cb8441f5c8f795202753fa6e5f Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Thu, 26 Jan 2023 16:38:08 -0500 Subject: [PATCH 100/146] third_party/zlib/CMakeLists.txt: Modernize crashpad_zlib interface --- third_party/zlib/CMakeLists.txt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index eeb449ff33..627b8352cb 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -1,15 +1,11 @@ if(CRASHPAD_ZLIB_SYSTEM) add_library(crashpad_zlib INTERFACE) - string(REPLACE ";" "$" GENEX_ZLIB_LIBRARIES "${ZLIB_LIBRARIES}") - target_include_directories(crashpad_zlib INTERFACE - $ - ) target_compile_definitions(crashpad_zlib INTERFACE ZLIB_CONST CRASHPAD_ZLIB_SOURCE_SYSTEM $ ) - target_link_libraries(crashpad_zlib INTERFACE $) + target_link_libraries(crashpad_zlib INTERFACE ZLIB::ZLIB) else() add_library(crashpad_zlib STATIC zlib/adler32.c From 59b37de134bf350aa14dde3ad76def0565e5d1d5 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Sat, 28 Jan 2023 11:26:28 -0500 Subject: [PATCH 101/146] Improve MSVC / MINGW compile definition parity --- CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 624aaaee26..cbb998436a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,7 +99,7 @@ target_compile_definitions(crashpad_interface INTERFACE CRASHPAD_LSS_SOURCE_EMBEDDED ) -if(MSVC) +if(WIN32) target_compile_definitions(crashpad_interface INTERFACE NOMINMAX UNICODE @@ -108,6 +108,8 @@ if(MSVC) _HAS_EXCEPTIONS=0 _UNICODE ) +endif() +if(MSVC) 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 From 08ec71c9282c1eb74f10175ed3c219d7282d5e34 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Fri, 3 Feb 2023 13:08:42 -0500 Subject: [PATCH 102/146] dbghelp.h: Silence Wzero-length-array --- compat/mingw/dbghelp.h | 1 + 1 file changed, 1 insertion(+) diff --git a/compat/mingw/dbghelp.h b/compat/mingw/dbghelp.h index bfaf5113d7..a7fdb4ef4e 100644 --- a/compat/mingw/dbghelp.h +++ b/compat/mingw/dbghelp.h @@ -17,6 +17,7 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wgnu-include-next" +#pragma clang diagnostic ignored "-Wzero-length-array" #include_next #include From 981e164617d04744ffb9225bc1c74779af9bc9ba Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Fri, 3 Feb 2023 13:13:15 -0500 Subject: [PATCH 103/146] [CMake] Adjust crashpad_compat link command --- handler/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 8c7a620435..0e5851c099 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -137,8 +137,7 @@ if(CRASHPAD_WER_ENABLED) target_link_libraries(crashpad_wer PRIVATE $ - PUBLIC - crashpad_compat + $ ) set_property(TARGET crashpad_wer PROPERTY EXPORT_NAME crashpad_wer) From 486c2937dd88ed42adeb98be4f668b2821a2749b Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Sat, 4 Feb 2023 12:42:15 +0100 Subject: [PATCH 104/146] fix: make sure we reuse already found libcurl --- util/CMakeLists.txt | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 9789ce58b0..426e2461f0 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -246,9 +246,22 @@ endif() if(LINUX OR ANDROID) if (LINUX) - find_package(CURL REQUIRED) - target_include_directories(crashpad_util PRIVATE ${CURL_INCLUDE_DIRS}) - target_link_libraries(crashpad_util PRIVATE ${CURL_LIBRARIES}) + 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(crashpad_util PRIVATE CURL::libcurl) + else() + # Needed for cmake < 3.12 support (cmake 3.12 introduced the target CURL::libcurl) + target_include_directories(crashpad_util 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(crashpad_util PRIVATE $) + target_compile_definitions(crashpad_util PRIVATE $) + endif() + SET(HTTP_TRANSPORT_IMPL net/http_transport_libcurl.cc) else() SET(HTTP_TRANSPORT_IMPL net/http_transport_socket.cc) From 5886ae41fa652394d5385887c58f5df5fdf68f05 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Mon, 6 Feb 2023 13:11:35 -0500 Subject: [PATCH 105/146] [CMake] Make process_reader_win define overrides more conditional --- snapshot/CMakeLists.txt | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/snapshot/CMakeLists.txt b/snapshot/CMakeLists.txt index c9488173d5..ccaaef67c1 100644 --- a/snapshot/CMakeLists.txt +++ b/snapshot/CMakeLists.txt @@ -224,14 +224,16 @@ if(WIN32) ) endif() - # Define NTDDI_VERSION >= NTDDI_WIN10_RS5 - so InitializeContext2 definition is always available in process_reader_win.cc - # NTDDI_WIN10_RS5 = 0x0A000006 /* ABRACADABRA_WIN10_RS5 */ - set_property( - SOURCE "win/process_reader_win.cc" - APPEND - PROPERTY COMPILE_DEFINITIONS - WINVER=0x0A00 _WIN32_WINNT=0x0A00 NTDDI_VERSION=0x0A000006 - ) + if (MINGW OR ("${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" STREQUAL "") OR (CMAKE_SYSTEM_VERSION LESS 10)) + # Define NTDDI_VERSION >= NTDDI_WIN10_RS5 - so InitializeContext2 definition is always available in process_reader_win.cc + # NTDDI_WIN10_RS5 = 0x0A000006 /* ABRACADABRA_WIN10_RS5 */ + set_property( + SOURCE "win/process_reader_win.cc" + APPEND + PROPERTY COMPILE_DEFINITIONS + WINVER=0x0A00 _WIN32_WINNT=0x0A00 NTDDI_VERSION=0x0A000006 + ) + endif() endif() if(APPLE AND NOT IOS AND CRASHPAD_ENABLE_STACKTRACE) From 3af012a9c957872aa3426e2c6aea0a3636620600 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 6 Feb 2023 22:20:57 +0100 Subject: [PATCH 106/146] Add new sources to build-scripts --- client/CMakeLists.txt | 2 ++ util/CMakeLists.txt | 3 +++ 2 files changed, 5 insertions(+) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4b35a0f187..1ed52149c6 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -8,6 +8,8 @@ add_library(crashpad_client STATIC crashpad_client.h crashpad_info.cc crashpad_info.h + length_delimited_ring_buffer.h + ring_buffer_annotation.h prune_crash_reports.cc prune_crash_reports.h settings.cc diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 426e2461f0..b8918f2e8d 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -104,6 +104,7 @@ add_library(crashpad_util STATIC stream/zlib_output_stream.h string/split_string.cc string/split_string.h + synchronization/scoped_spin_guard.h synchronization/semaphore.h thread/stoppable.h thread/thread.cc @@ -225,6 +226,8 @@ if(APPLE) ios/scoped_background_task.mm ios/scoped_vm_read.cc ios/scoped_vm_read.h + ios/scoped_vm_map.cc + ios/scoped_vm_map.h ) # This specific file requires ARC support, while other parts do not # build when ARC is enabled. From ee62397fd057c1a29016e2530d627362b340382c Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 27 Feb 2023 09:11:43 +0100 Subject: [PATCH 107/146] fix: remove the OpenSSL dependency for Linux Since we no longer use `http_transport_socket` on Linux there is no need to depend on OpenSSL directly anymore. We use whatever the present `libcurl` implementation uses as its TLS implementation. Also moved the dependency discovery for Android (which we currently do not use) to `crashpad-util` to isolate it to its usage. --- CMakeLists.txt | 8 -------- util/CMakeLists.txt | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbb998436a..d34b8f7436 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,14 +25,6 @@ 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() - if (NOT (ANDROID OR FUCHSIA)) add_compile_definitions(CRASHPAD_FLOCK_ALWAYS_SUPPORTED=1) else() diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index b8918f2e8d..e036307d58 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -267,6 +267,10 @@ if(LINUX OR ANDROID) SET(HTTP_TRANSPORT_IMPL net/http_transport_libcurl.cc) else() + find_package(OpenSSL) + if(OPENSSL_FOUND) + set(CRASHPAD_USE_BORINGSSL ON) + endif() SET(HTTP_TRANSPORT_IMPL net/http_transport_socket.cc) endif() From fbd4abeab0a9412336bbce66bad5ffcdc19903dc Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Wed, 3 May 2023 23:35:59 +0200 Subject: [PATCH 108/146] Add PAC helper to Linux utils build. --- util/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index e036307d58..ad676b793c 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -289,6 +289,8 @@ if(LINUX OR ANDROID) linux/exception_information.h linux/memory_map.cc linux/memory_map.h + linux/pac_helper.cc + linux/pac_helper.h linux/proc_stat_reader.cc linux/proc_stat_reader.h linux/proc_task_reader.cc From 56b62ee54a62f65eb5f4e2b2f14bf48f2d1ae932 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Wed, 3 May 2023 23:37:36 +0200 Subject: [PATCH 109/146] Add dump_minidump_annotations to tools build. Also: clean up and fix the failing tool-build on macOS. --- tools/CMakeLists.txt | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 5ad79d5d5e..92a8330d2d 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -12,6 +12,22 @@ add_library(crashpad::tools ALIAS crashpad_tools) crashpad_install_target(crashpad_tools) if(CRASHPAD_BUILD_TOOLS) + add_executable(dump_minidump_annotations + dump_minidump_annotations.cc + ) + target_link_libraries(dump_minidump_annotations PRIVATE + crashpad_client + crashpad_snapshot + crashpad_util + crashpad_tools + ) + crashpad_install_target(dump_minidump_annotations) + if(MSVC) + target_compile_options(dump_minidump_annotations PRIVATE + $<$:/wd4201> # nonstandard extension used : nameless struct/union + ) + endif() + add_executable(crashpad_database_util crashpad_database_util.cc ) @@ -46,16 +62,18 @@ if(CRASHPAD_BUILD_TOOLS) crashpad_tools mini_chromium ) - if(APPLE) - # FIXME: cmake 3.13 added target_link_options - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sectcreate __TEXT __info_plist \"${CMAKE_CURRENT_SOURCE_DIR}/mac/sectaskaccess_info.plist\"") - endif() crashpad_install_target(crashpad_generate_dump) if(APPLE) + function(setup_apple_tool target) + target_link_options(${target} PRIVATE LINKER:-sectcreate,__TEXT,__info_plist,${CMAKE_CURRENT_SOURCE_DIR}/mac/sectaskaccess_info.plist) + target_include_directories(${target} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") + endfunction() + add_executable(run_with_crashpad run_with_crashpad.cc ) + setup_apple_tool(run_with_crashpad) target_link_libraries(run_with_crashpad PRIVATE crashpad_client crashpad_compat @@ -68,6 +86,7 @@ if(CRASHPAD_BUILD_TOOLS) add_executable(catch_exception_tool mac/catch_exception_tool.cc ) + setup_apple_tool(catch_exception_tool) target_link_libraries(catch_exception_tool PRIVATE crashpad_compat crashpad_tools @@ -79,22 +98,21 @@ if(CRASHPAD_BUILD_TOOLS) add_executable(exception_port_tool mac/exception_port_tool.cc ) + setup_apple_tool(exception_port_tool) target_link_libraries(exception_port_tool PRIVATE crashpad_compat crashpad_tools crashpad_util mini_chromium ) - # FIXME: cmake 3.13 added target_link_options - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -sectcreate __TEXT __info_plist \"${CMAKE_CURRENT_SOURCE_DIR}/mac/sectaskaccess_info.plist\"") - crashpad_install_target(exception_port_tool) add_executable(on_demand_service_tool mac/on_demand_service_tool.mm ) + setup_apple_tool(on_demand_service_tool) target_link_libraries(on_demand_service_tool PRIVATE - -framework CoreFoundation - -framework Foundation + "-framework CoreFoundation" + "-framework Foundation" crashpad_compat crashpad_tools crashpad_util From fc014d91be0ba60c791c6258eb43932f927fa8e1 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 4 May 2023 10:11:49 +0200 Subject: [PATCH 110/146] Add tools to CI build --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cbefe8feaa..ad02072f84 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: - name: Build crashpad run: | - cmake -B cmake-build + cmake -B cmake-build -D CRASHPAD_BUILD_TOOLS=On cmake --build cmake-build --parallel - name: Build crashpad with client-side stack traces From 36b0f0d9499c6bb1b7ea0064659287ad5f6012b1 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 4 May 2023 12:12:04 +0200 Subject: [PATCH 111/146] Fix windows tool build --- tools/CMakeLists.txt | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 92a8330d2d..4dfd7b2f67 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -19,14 +19,19 @@ if(CRASHPAD_BUILD_TOOLS) crashpad_client crashpad_snapshot crashpad_util + crashpad_getopt crashpad_tools ) - crashpad_install_target(dump_minidump_annotations) if(MSVC) target_compile_options(dump_minidump_annotations PRIVATE $<$:/wd4201> # nonstandard extension used : nameless struct/union ) + target_compile_definitions(dump_minidump_annotations PRIVATE + WIN32_LEAN_AND_MEAN + NOMINMAX + ) endif() + crashpad_install_target(dump_minidump_annotations) add_executable(crashpad_database_util crashpad_database_util.cc @@ -37,6 +42,12 @@ if(CRASHPAD_BUILD_TOOLS) crashpad_getopt crashpad_tools ) + if(MSVC) + target_compile_definitions(crashpad_database_util PRIVATE + WIN32_LEAN_AND_MEAN + NOMINMAX + ) + endif() crashpad_install_target(crashpad_database_util) add_executable(crashpad_http_upload @@ -50,6 +61,12 @@ if(CRASHPAD_BUILD_TOOLS) crashpad_zlib mini_chromium ) + if(MSVC) + target_compile_definitions(crashpad_http_upload PRIVATE + WIN32_LEAN_AND_MEAN + NOMINMAX + ) + endif() crashpad_install_target(crashpad_http_upload) add_executable(crashpad_generate_dump @@ -62,6 +79,12 @@ if(CRASHPAD_BUILD_TOOLS) crashpad_tools mini_chromium ) + if(MSVC) + target_compile_definitions(crashpad_generate_dump PRIVATE + WIN32_LEAN_AND_MEAN + NOMINMAX + ) + endif() crashpad_install_target(crashpad_generate_dump) if(APPLE) From 53c2ede36ed7626731fd22141cb4f11105b8f5ad Mon Sep 17 00:00:00 2001 From: Ashley Camba Garrido Date: Thu, 4 May 2023 15:56:12 +0200 Subject: [PATCH 112/146] meta: Add codeowners --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..7afbb99cb6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @getsentry/owners-native From e67d687b6a6e6a7662b1ae6e79ab7751a07b85ef Mon Sep 17 00:00:00 2001 From: xyz1001 Date: Fri, 19 May 2023 18:11:23 +0800 Subject: [PATCH 113/146] Fix compiler error with msvc on non-unicode system --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index d34b8f7436..88e14d2dc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,9 @@ if(WIN32) ) endif() if(MSVC) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /utf-8") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8") + 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 From dc779354ccf020a3155ea1fac4534a9345ccdfa2 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Sat, 20 May 2023 20:25:15 +0200 Subject: [PATCH 114/146] Initial e2e implementation macOS --- client/crashpad_client.h | 1 + client/crashpad_client_mac.cc | 38 ++++++--- handler/crash_report_upload_thread.cc | 10 ++- handler/crash_report_upload_thread.h | 4 +- handler/handler_main.cc | 9 +++ util/net/http_transport.cc | 4 + util/net/http_transport.h | 7 ++ util/net/http_transport_libcurl.cc | 3 + util/net/http_transport_mac.mm | 112 ++++++++++++++++---------- util/net/http_transport_win.cc | 19 ++++- 10 files changed, 146 insertions(+), 61 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 2cb8eb99c8..679dd6a7ca 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -124,6 +124,7 @@ class CrashpadClient { const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, bool restartable, diff --git a/client/crashpad_client_mac.cc b/client/crashpad_client_mac.cc index 17cee8ecc5..9bddc1ea4d 100644 --- a/client/crashpad_client_mac.cc +++ b/client/crashpad_client_mac.cc @@ -92,8 +92,7 @@ bool SetCrashExceptionPorts(exception_handler_t exception_handler) { class ScopedPthreadAttrDestroy { public: explicit ScopedPthreadAttrDestroy(pthread_attr_t* pthread_attr) - : pthread_attr_(pthread_attr) { - } + : pthread_attr_(pthread_attr) {} ScopedPthreadAttrDestroy(const ScopedPthreadAttrDestroy&) = delete; ScopedPthreadAttrDestroy& operator=(const ScopedPthreadAttrDestroy&) = delete; @@ -127,6 +126,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments, @@ -167,6 +167,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { database, metrics_dir, url, + http_proxy, annotations, arguments, attachments, @@ -177,8 +178,14 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { } if (handler_restarter && - handler_restarter->StartRestartThread( - handler, database, metrics_dir, url, annotations, arguments, attachments)) { + handler_restarter->StartRestartThread(handler, + database, + metrics_dir, + url, + http_proxy, + annotations, + arguments, + attachments)) { // The thread owns the object now. std::ignore = handler_restarter.release(); } @@ -211,6 +218,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { database_, metrics_dir_, url_, + http_proxy_, annotations_, arguments_, attachments_, @@ -232,8 +240,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { arguments_(), attachments_(), notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), - last_start_time_(0) { - } + last_start_time_(0) {} //! \brief Starts a Crashpad handler. //! @@ -258,6 +265,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments, @@ -337,6 +345,9 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { if (!url.empty()) { argv.push_back(FormatArgumentString("url", url)); } + if (!http_proxy.empty()) { + argv.push_back(FormatArgumentString("http-proxy", http_proxy)); + } for (const auto& kv : annotations) { argv.push_back( FormatArgumentString("annotation", kv.first + '=' + kv.second)); @@ -380,6 +391,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments) { @@ -387,6 +399,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { database_ = database; metrics_dir_ = metrics_dir; url_ = url; + http_proxy_ = http_proxy; annotations_ = annotations; arguments_ = arguments; attachments_ = attachments; @@ -440,6 +453,7 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { base::FilePath database_; base::FilePath metrics_dir_; std::string url_; + std::string http_proxy_; std::map annotations_; std::vector arguments_; std::vector attachments_; @@ -449,17 +463,16 @@ class HandlerStarter final : public NotifyServer::DefaultInterface { } // namespace -CrashpadClient::CrashpadClient() : exception_port_(MACH_PORT_NULL) { -} +CrashpadClient::CrashpadClient() : exception_port_(MACH_PORT_NULL) {} -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::string& http_proxy, const std::map& annotations, const std::vector& arguments, bool restartable, @@ -473,6 +486,7 @@ bool CrashpadClient::StartHandler( database, metrics_dir, url, + http_proxy, annotations, arguments, attachments, @@ -538,8 +552,8 @@ base::mac::ScopedMachSendRight CrashpadClient::GetHandlerMachPort() const { // static void CrashpadClient::UseSystemDefaultHandler() { - base::mac::ScopedMachSendRight - system_crash_reporter_handler(SystemCrashReporterHandler()); + base::mac::ScopedMachSendRight system_crash_reporter_handler( + SystemCrashReporterHandler()); // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL // to clear the current exception ports. diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index 5bd2889eda..7cea398e1b 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -20,6 +20,7 @@ #include #include #include +#include #include #include "base/logging.h" @@ -88,12 +89,14 @@ class ScopedFunctionInvoker final { CrashReportUploadThread::CrashReportUploadThread( CrashReportDatabase* database, - const std::string& url, + std::string url, + std::string http_proxy, const Options& options, ProcessPendingReportsObservationCallback callback) : options_(options), - callback_(callback), - url_(url), + callback_(std::move(callback)), + url_(std::move(url)), + http_proxy_(std::move(http_proxy)), // 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 @@ -365,6 +368,7 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( } } http_transport->SetURL(url); + http_transport->SetHTTPProxy(http_proxy_); if (!http_transport->ExecuteSynchronously(response_body)) { return UploadResult::kRetry; diff --git a/handler/crash_report_upload_thread.h b/handler/crash_report_upload_thread.h index 22bb26e318..6bde3835b7 100644 --- a/handler/crash_report_upload_thread.h +++ b/handler/crash_report_upload_thread.h @@ -85,7 +85,8 @@ class CrashReportUploadThread : public WorkerThread::Delegate, //! processing and attempting to upload on-disk crash reports. //! If this callback is empty, it is not invoked. CrashReportUploadThread(CrashReportDatabase* database, - const std::string& url, + std::string url, + std::string http_proxy, const Options& options, ProcessPendingReportsObservationCallback callback); @@ -226,6 +227,7 @@ class CrashReportUploadThread : public WorkerThread::Delegate, const Options options_; const ProcessPendingReportsObservationCallback callback_; const std::string url_; + const std::string http_proxy_; WorkerThread thread_; ThreadSafeVector known_pending_report_uuids_; #if BUILDFLAG(IS_IOS) diff --git a/handler/handler_main.cc b/handler/handler_main.cc index 7f875b9e94..e45b77d3c0 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -223,6 +223,7 @@ struct Options { std::map annotations; std::map monitor_self_annotations; std::string url; + std::string http_proxy; base::FilePath database; base::FilePath metrics_dir; std::vector monitor_self_arguments; @@ -515,6 +516,7 @@ void MonitorSelf(const Options& options) { options.database, base::FilePath(), options.url, + options.http_proxy, options.annotations, extra_arguments, true, @@ -621,6 +623,7 @@ int HandlerMain(int argc, kOptionTraceParentWithException, #endif kOptionURL, + kOptionHttpProxy, #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) kOptionUseCrosCrashReporter, kOptionMinidumpDirForTests, @@ -705,6 +708,7 @@ int HandlerMain(int argc, #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || // BUILDFLAG(IS_ANDROID) {"url", required_argument, nullptr, kOptionURL}, + {"http-proxy", optional_argument, nullptr, kOptionHttpProxy}, #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) {"use-cros-crash-reporter", no_argument, @@ -878,6 +882,10 @@ int HandlerMain(int argc, options.url = optarg; break; } + case kOptionHttpProxy: { + options.http_proxy = optarg; + break; + } #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) case kOptionUseCrosCrashReporter: { options.use_cros_crash_reporter = true; @@ -1030,6 +1038,7 @@ int HandlerMain(int argc, upload_thread.Reset(new CrashReportUploadThread( database.get(), options.url, + options.http_proxy, upload_thread_options, CrashReportUploadThread::ProcessPendingReportsObservationCallback())); upload_thread.Get()->Start(); diff --git a/util/net/http_transport.cc b/util/net/http_transport.cc index dcc8d80a59..6c1a9b0870 100644 --- a/util/net/http_transport.cc +++ b/util/net/http_transport.cc @@ -35,6 +35,10 @@ void HTTPTransport::SetURL(const std::string& url) { url_ = url; } +void HTTPTransport::SetHTTPProxy(const std::string& http_proxy) { + http_proxy_ = http_proxy; +} + void HTTPTransport::SetMethod(const std::string& method) { method_ = method; } diff --git a/util/net/http_transport.h b/util/net/http_transport.h index b046173b68..49742b85ca 100644 --- a/util/net/http_transport.h +++ b/util/net/http_transport.h @@ -50,6 +50,11 @@ class HTTPTransport { //! \param[in] url The request URL. void SetURL(const std::string& url); + //! \brief Sets the optional HTTP proxy through which to send the request + //! + //! \param[in] http_proxy the fully specified URL + void SetHTTPProxy(const std::string& http_proxy); + //! \brief Sets the HTTP method to execute. E.g., GET, POST, etc. The default //! method is `"POST"`. //! @@ -99,6 +104,7 @@ class HTTPTransport { HTTPTransport(); const std::string& url() const { return url_; } + const std::string& http_proxy() const { return http_proxy_; } const std::string& method() const { return method_; } const HTTPHeaders& headers() const { return headers_; } HTTPBodyStream* body_stream() const { return body_stream_.get(); } @@ -109,6 +115,7 @@ class HTTPTransport { private: std::string url_; + std::string http_proxy_; std::string method_; base::FilePath root_ca_certificate_path_; HTTPHeaders headers_; diff --git a/util/net/http_transport_libcurl.cc b/util/net/http_transport_libcurl.cc index d5c9177f95..0bb3656d87 100644 --- a/util/net/http_transport_libcurl.cc +++ b/util/net/http_transport_libcurl.cc @@ -456,6 +456,9 @@ bool HTTPTransportLibcurl::ExecuteSynchronously(std::string* response_body) { TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_READDATA, this); TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEFUNCTION, WriteResponseBody); TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEDATA, response_body); + if (!http_proxy().empty()) { + TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_PROXY, http_proxy()); + } #undef TRY_CURL_EASY_SETOPT #undef TRY_CURL_SLIST_APPEND diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index 04f5c34798..be2aee62a5 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -27,6 +27,7 @@ #include "util/misc/implicit_cast.h" #include "util/misc/metrics.h" #include "util/net/http_body.h" +#include "util/net/url.h" // An implementation of NSInputStream that reads from a // crashpad::HTTPBodyStream. @@ -210,15 +211,15 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { bool ExecuteSynchronously(std::string* response_body) override; }; -HTTPTransportMac::HTTPTransportMac() : HTTPTransport() { -} +HTTPTransportMac::HTTPTransportMac() : HTTPTransport() {} -HTTPTransportMac::~HTTPTransportMac() { -} +HTTPTransportMac::~HTTPTransportMac() {} bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) { DCHECK(body_stream()); + __block bool sync_rv = false; + @autoreleasepool { NSString* url_ns_string = base::SysUTF8ToNSString(url()); NSURL* url = [NSURL URLWithString:url_ns_string]; @@ -245,54 +246,77 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { initWithBodyStream:body_stream()]); [request setHTTPBodyStream:input_stream.get()]; - NSURLResponse* response = nil; - NSError* error = nil; -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - // Deprecated in OS X 10.11. The suggested replacement, NSURLSession, is - // only available on 10.9 and later, and this needs to run on earlier - // releases. - NSData* body = [NSURLConnection sendSynchronousRequest:request - returningResponse:&response - error:&error]; -#pragma clang diagnostic pop - - if (error) { - Metrics::CrashUploadErrorCode(error.code); - LOG(ERROR) << [[error localizedDescription] UTF8String] << " (" - << [[error domain] UTF8String] << " " << [error code] << ")"; - return false; - } - if (!response) { - LOG(ERROR) << "no response"; - return false; - } - NSHTTPURLResponse* http_response = - base::mac::ObjCCast(response); - if (!http_response) { - LOG(ERROR) << "no http_response"; - return false; - } - NSInteger http_status = [http_response statusCode]; - if (http_status < 200 || http_status > 203) { - LOG(ERROR) << base::StringPrintf("HTTP status %ld", - implicit_cast(http_status)); - return false; - } - - if (response_body) { - response_body->assign(static_cast([body bytes]), - [body length]); + NSURLSessionConfiguration* sessionConfig = + [NSURLSessionConfiguration ephemeralSessionConfiguration]; + + if (!http_proxy().empty()) { + std::string scheme, host, port, rest_ignored; + CrackURL(http_proxy(), &scheme, &host, &port, &rest_ignored); + NSString* schemeNS = base::SysUTF8ToNSString(scheme); + NSString* hostNS = base::SysUTF8ToNSString(host); + NSNumber* proxy_port = @(std::stoi(port)); + + NSDictionary* proxyDict = @{ + (__bridge id)kCFNetworkProxiesHTTPEnable : @YES, + (__bridge id)kCFNetworkProxiesHTTPPort : proxy_port, + (__bridge id)kCFNetworkProxiesHTTPProxy : hostNS, + }; + sessionConfig.connectionProxyDictionary = proxyDict; } - return true; + NSURLSession* session = + [NSURLSession sessionWithConfiguration:sessionConfig]; + + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + [[session dataTaskWithRequest:request + completionHandler:^( + NSData* body, NSURLResponse* response, NSError* error) { + if (error) { + Metrics::CrashUploadErrorCode(error.code); + LOG(ERROR) << [[error localizedDescription] UTF8String] + << " (" << [[error domain] UTF8String] << " " + << [error code] << ")"; + sync_rv = false; + return; + } + if (!response) { + LOG(ERROR) << "no response"; + sync_rv = false; + return; + } + NSHTTPURLResponse* http_response = + base::mac::ObjCCast(response); + if (!http_response) { + LOG(ERROR) << "no http_response"; + sync_rv = false; + return; + } + NSInteger http_status = [http_response statusCode]; + if (http_status < 200 || http_status > 203) { + LOG(ERROR) << base::StringPrintf( + "HTTP status %ld", implicit_cast(http_status)); + sync_rv = false; + return; + } + + if (response_body) { + response_body->assign( + static_cast([body bytes]), [body length]); + } + sync_rv = true; + dispatch_semaphore_signal(semaphore); + }] resume]; + + dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); } + + return sync_rv; } } // namespace // static -std::unique_ptr HTTPTransport::Create() { +std::unique_ptr crashpad::HTTPTransport::Create() { return std::unique_ptr(new HTTPTransportMac()); } diff --git a/util/net/http_transport_win.cc b/util/net/http_transport_win.cc index 6bc2481d7a..7561433a62 100644 --- a/util/net/http_transport_win.cc +++ b/util/net/http_transport_win.cc @@ -148,11 +148,28 @@ HTTPTransportWin::~HTTPTransportWin() { } bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) { - ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), + // ensure the proxy starts with `http://`, otherwise ignore it + if (http_proxy().rfind("http://", 0) == 0) { + size_t next_slash_pos = http_proxy().find("/", 7) + std::string proxy = std::whttp_proxy().substr(7, next_slash_pos != std::string::npos ? next_slash_pos : std::string::npos); + std::wstring proxy_wstr = std::wstring_convert>().from_bytes(proxy); + ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), WINHTTP_ACCESS_TYPE_NAMED_PROXY, + proxy_wstr, WINHTTP_NO_PROXY_BYPASS, 0)); + } else { +#if _WIN32_WINNT >= 0x0603 + ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, 0)); +#else + // On windows 8.0 or lower, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY does + // not work on error we fallback to WINHTTP_ACCESS_TYPE_DEFAULT_PROXY + ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0)); + +#endif if (!session.get()) { LOG(ERROR) << WinHttpMessage("WinHttpOpen"); return false; From 96b7d5f5802638a99174647012f118f05f572379 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 12:24:36 +0200 Subject: [PATCH 115/146] Make http_proxy work on Windows --- client/crashpad_client_win.cc | 10 ++++++ util/net/http_transport_win.cc | 57 ++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 24 deletions(-) diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 0bb7aa89c0..6aa956ab8f 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -331,6 +331,7 @@ struct BackgroundHandlerStartThreadData { const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments, @@ -340,6 +341,7 @@ struct BackgroundHandlerStartThreadData { database(database), metrics_dir(metrics_dir), url(url), + http_proxy(http_proxy), annotations(annotations), arguments(arguments), attachments(attachments), @@ -350,6 +352,7 @@ struct BackgroundHandlerStartThreadData { base::FilePath database; base::FilePath metrics_dir; std::string url; + std::string http_proxy; std::map annotations; std::vector arguments; std::vector attachments; @@ -405,6 +408,11 @@ bool StartHandlerProcess( FormatArgumentString("url", base::UTF8ToWide(data->url)), &command_line); } + if (!data->http_proxy.empty()) { + AppendCommandLineArgument( + FormatArgumentString("http-proxy", base::UTF8ToWide(data->http_proxy)), + &command_line); + } for (const auto& kv : data->annotations) { AppendCommandLineArgument( FormatArgumentString("annotation", @@ -629,6 +637,7 @@ bool CrashpadClient::StartHandler( const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, bool restartable, @@ -661,6 +670,7 @@ bool CrashpadClient::StartHandler( database, metrics_dir, url, + http_proxy, annotations, arguments, attachments, diff --git a/util/net/http_transport_win.cc b/util/net/http_transport_win.cc index 7561433a62..d4eecb44ee 100644 --- a/util/net/http_transport_win.cc +++ b/util/net/http_transport_win.cc @@ -23,6 +23,7 @@ #include #include +#include #include "base/logging.h" #include "base/numerics/safe_conversions.h" @@ -115,9 +116,7 @@ std::string WinHttpMessage(const char* extra) { } struct ScopedHINTERNETTraits { - static HINTERNET InvalidValue() { - return nullptr; - } + static HINTERNET InvalidValue() { return nullptr; } static void Free(HINTERNET handle) { if (handle) { if (!WinHttpCloseHandle(handle)) { @@ -141,35 +140,46 @@ class HTTPTransportWin final : public HTTPTransport { bool ExecuteSynchronously(std::string* response_body) override; }; -HTTPTransportWin::HTTPTransportWin() : HTTPTransport() { -} +HTTPTransportWin::HTTPTransportWin() : HTTPTransport() {} -HTTPTransportWin::~HTTPTransportWin() { -} +HTTPTransportWin::~HTTPTransportWin() {} bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) { // ensure the proxy starts with `http://`, otherwise ignore it - if (http_proxy().rfind("http://", 0) == 0) { - size_t next_slash_pos = http_proxy().find("/", 7) - std::string proxy = std::whttp_proxy().substr(7, next_slash_pos != std::string::npos ? next_slash_pos : std::string::npos); - std::wstring proxy_wstr = std::wstring_convert>().from_bytes(proxy); - ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), WINHTTP_ACCESS_TYPE_NAMED_PROXY, - proxy_wstr, WINHTTP_NO_PROXY_BYPASS, 0)); + const char proto[] = "http://"; + ScopedHINTERNET session; + if (http_proxy().rfind(proto, 0) == 0) { + size_t proto_len = sizeof(proto) - 1; + size_t next_slash_pos = http_proxy().find('/', proto_len); + std::string proxy = http_proxy().substr(proto_len, + next_slash_pos != std::string::npos + ? next_slash_pos - proto_len + : std::string::npos); + session = ScopedHINTERNET(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), + WINHTTP_ACCESS_TYPE_NAMED_PROXY, + base::UTF8ToWide(proxy).c_str(), + WINHTTP_NO_PROXY_BYPASS, + 0)); } else { #if _WIN32_WINNT >= 0x0603 - ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), - WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, WINHTTP_NO_PROXY_NAME, - WINHTTP_NO_PROXY_BYPASS, 0)); -#else - // On windows 8.0 or lower, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY does - // not work on error we fallback to WINHTTP_ACCESS_TYPE_DEFAULT_PROXY - ScopedHINTERNET session(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), + session = ScopedHINTERNET(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), + WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, + WINHTTP_NO_PROXY_NAME, + WINHTTP_NO_PROXY_BYPASS, + 0)); +#endif + // On Windows 8.0 or lower, WINHTTP_ACCESS_TYPE_AUTOMATIC_PROXY does + // not work on error we fall back to WINHTTP_ACCESS_TYPE_DEFAULT_PROXY + if (!session.get()) { + session = + ScopedHINTERNET(WinHttpOpen(base::UTF8ToWide(UserAgent()).c_str(), WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0)); + } + } -#endif if (!session.get()) { LOG(ERROR) << WinHttpMessage("WinHttpOpen"); return false; @@ -192,8 +202,7 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) { url_components.dwExtraInfoLength = 1; std::wstring url_wide(base::UTF8ToWide(url())); // dwFlags = ICU_REJECT_USERPWD fails on XP. - if (!WinHttpCrackUrl( - url_wide.c_str(), 0, 0, &url_components)) { + if (!WinHttpCrackUrl(url_wide.c_str(), 0, 0, &url_components)) { LOG(ERROR) << WinHttpMessage("WinHttpCrackUrl"); return false; } @@ -429,7 +438,7 @@ bool HTTPTransportWin::ExecuteSynchronously(std::string* response_body) { // static std::unique_ptr HTTPTransport::Create() { - return std::unique_ptr(new HTTPTransportWin); + return std::make_unique(); } } // namespace crashpad From 3bc2282608a448645a3a8c153ef8e4705bfb221c Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 14:44:27 +0200 Subject: [PATCH 116/146] Fix up http-proxy pass through from client to handler on Linux --- client/client_argv_handling.cc | 5 +++++ client/client_argv_handling.h | 1 + client/crashpad_client.h | 6 ++++++ client/crashpad_client_linux.cc | 9 ++++++--- util/net/http_transport_libcurl.cc | 2 +- 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/client/client_argv_handling.cc b/client/client_argv_handling.cc index 766812cdfa..af7270b386 100644 --- a/client/client_argv_handling.cc +++ b/client/client_argv_handling.cc @@ -32,6 +32,7 @@ std::vector BuildHandlerArgvStrings( const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments) { @@ -54,6 +55,10 @@ std::vector BuildHandlerArgvStrings( argv_strings.push_back(FormatArgumentString("url", url)); } + if (!http_proxy.empty()) { + argv_strings.push_back(FormatArgumentString("http-proxy", http_proxy)); + } + for (const auto& kv : annotations) { argv_strings.push_back( FormatArgumentString("annotation", kv.first + '=' + kv.second)); diff --git a/client/client_argv_handling.h b/client/client_argv_handling.h index ba05bd3521..d3c083f7a2 100644 --- a/client/client_argv_handling.h +++ b/client/client_argv_handling.h @@ -34,6 +34,7 @@ std::vector BuildHandlerArgvStrings( const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments = {}); diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 679dd6a7ca..78647c068d 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -364,6 +364,8 @@ class CrashpadClient { //! 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] http_proxy The URL of an HTTP proxy. The handler will be started + //! with this URL as its `--http-proxy` 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. @@ -378,6 +380,7 @@ class CrashpadClient { const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments = {}); @@ -395,6 +398,8 @@ class CrashpadClient { //! 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] http_proxy The URL of an HTTP proxy. The handler will be started + //! with this URL as its `--http-proxy` 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. @@ -411,6 +416,7 @@ class CrashpadClient { const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, int socket); diff --git a/client/crashpad_client_linux.cc b/client/crashpad_client_linux.cc index 01a10008ea..e48c60f349 100644 --- a/client/crashpad_client_linux.cc +++ b/client/crashpad_client_linux.cc @@ -443,6 +443,7 @@ bool CrashpadClient::StartHandler( const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, bool restartable, @@ -457,7 +458,7 @@ bool CrashpadClient::StartHandler( } std::vector argv = BuildHandlerArgvStrings( - handler, database, metrics_dir, url, annotations, arguments, attachments); + handler, database, metrics_dir, url, http_proxy, annotations, arguments, attachments); argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get())); argv.push_back("--shared-client-connection"); @@ -676,11 +677,12 @@ bool CrashpadClient::StartHandlerAtCrash( const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, const std::vector& attachments) { std::vector argv = BuildHandlerArgvStrings( - handler, database, metrics_dir, url, annotations, arguments, attachments); + handler, database, metrics_dir, url, http_proxy, annotations, arguments, attachments); auto signal_handler = LaunchAtCrashHandler::Get(); return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_); @@ -692,11 +694,12 @@ bool CrashpadClient::StartHandlerForClient( const base::FilePath& database, const base::FilePath& metrics_dir, const std::string& url, + const std::string& http_proxy, const std::map& annotations, const std::vector& arguments, int socket) { std::vector argv = BuildHandlerArgvStrings( - handler, database, metrics_dir, url, annotations, arguments); + handler, database, metrics_dir, url, http_proxy, annotations, arguments); argv.push_back(FormatArgumentInt("initial-client-fd", socket)); diff --git a/util/net/http_transport_libcurl.cc b/util/net/http_transport_libcurl.cc index 0bb3656d87..c8d8ee37cc 100644 --- a/util/net/http_transport_libcurl.cc +++ b/util/net/http_transport_libcurl.cc @@ -457,7 +457,7 @@ bool HTTPTransportLibcurl::ExecuteSynchronously(std::string* response_body) { TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEFUNCTION, WriteResponseBody); TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_WRITEDATA, response_body); if (!http_proxy().empty()) { - TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_PROXY, http_proxy()); + TRY_CURL_EASY_SETOPT(curl.get(), CURLOPT_PROXY, http_proxy().c_str()); } #undef TRY_CURL_EASY_SETOPT From af58387d057f04cf676ba0ee9a28cb0715a67715 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 18:16:25 +0200 Subject: [PATCH 117/146] Make sure dispatch_semaphore_signal is called when request errors --- util/net/http_transport_mac.mm | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index be2aee62a5..57c7ff5f23 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -250,8 +250,9 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { [NSURLSessionConfiguration ephemeralSessionConfiguration]; if (!http_proxy().empty()) { + std::string proxy = http_proxy() + "/"; std::string scheme, host, port, rest_ignored; - CrackURL(http_proxy(), &scheme, &host, &port, &rest_ignored); + CrackURL(proxy, &scheme, &host, &port, &rest_ignored); NSString* schemeNS = base::SysUTF8ToNSString(scheme); NSString* hostNS = base::SysUTF8ToNSString(host); NSNumber* proxy_port = @(std::stoi(port)); @@ -260,6 +261,9 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { (__bridge id)kCFNetworkProxiesHTTPEnable : @YES, (__bridge id)kCFNetworkProxiesHTTPPort : proxy_port, (__bridge id)kCFNetworkProxiesHTTPProxy : hostNS, + (__bridge id)kCFNetworkProxiesHTTPSEnable : @YES, + (__bridge id)kCFNetworkProxiesHTTPSPort : proxy_port, + (__bridge id)kCFNetworkProxiesHTTPSProxy : hostNS, }; sessionConfig.connectionProxyDictionary = proxyDict; } @@ -277,18 +281,21 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { << " (" << [[error domain] UTF8String] << " " << [error code] << ")"; sync_rv = false; + dispatch_semaphore_signal(semaphore); return; } if (!response) { LOG(ERROR) << "no response"; sync_rv = false; + dispatch_semaphore_signal(semaphore); return; } - NSHTTPURLResponse* http_response = + auto http_response = base::mac::ObjCCast(response); if (!http_response) { LOG(ERROR) << "no http_response"; sync_rv = false; + dispatch_semaphore_signal(semaphore); return; } NSInteger http_status = [http_response statusCode]; @@ -296,6 +303,7 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { LOG(ERROR) << base::StringPrintf( "HTTP status %ld", implicit_cast(http_status)); sync_rv = false; + dispatch_semaphore_signal(semaphore); return; } @@ -307,7 +315,7 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { dispatch_semaphore_signal(semaphore); }] resume]; - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); + dispatch_semaphore_wait(semaphore, (dispatch_time_t)(10 * NSEC_PER_SEC)); } return sync_rv; From e29852cd6e4dfbe0510894e86d844f4bffbfec96 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 18:47:08 +0200 Subject: [PATCH 118/146] Make the ios-build happy about the proxy-settings. We don't support crashpad on ios. --- util/net/http_transport_mac.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index 57c7ff5f23..9e796a247c 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -261,9 +261,9 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { (__bridge id)kCFNetworkProxiesHTTPEnable : @YES, (__bridge id)kCFNetworkProxiesHTTPPort : proxy_port, (__bridge id)kCFNetworkProxiesHTTPProxy : hostNS, - (__bridge id)kCFNetworkProxiesHTTPSEnable : @YES, - (__bridge id)kCFNetworkProxiesHTTPSPort : proxy_port, - (__bridge id)kCFNetworkProxiesHTTPSProxy : hostNS, + (__bridge id)@"HTTPSEnable" : @YES, + (__bridge id)@"HTTPSPort" : proxy_port, + (__bridge id)@"HTTPSProxy" : hostNS, }; sessionConfig.connectionProxyDictionary = proxyDict; } From 98412e9c09683f8d8bf0c2316df60bbb8d958e91 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 18:47:45 +0200 Subject: [PATCH 119/146] Add --http-proxy to run_with_crashpad tool --- tools/run_with_crashpad.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/run_with_crashpad.cc b/tools/run_with_crashpad.cc index 1f60081d6d..59abff1e9c 100644 --- a/tools/run_with_crashpad.cc +++ b/tools/run_with_crashpad.cc @@ -102,6 +102,7 @@ int RunWithCrashpadMain(int argc, char* argv[]) { kOptionAnnotation, kOptionDatabase, kOptionURL, + kOptionHTTPProxy, // Standard options. kOptionHelp = -2, @@ -113,6 +114,7 @@ int RunWithCrashpadMain(int argc, char* argv[]) { {"annotation", required_argument, nullptr, kOptionAnnotation}, {"database", required_argument, nullptr, kOptionDatabase}, {"url", required_argument, nullptr, kOptionURL}, + {"http-proxy", optional_argument, nullptr, kOptionHTTPProxy}, {"argument", required_argument, nullptr, kOptionArgument}, {"help", no_argument, nullptr, kOptionHelp}, {"version", no_argument, nullptr, kOptionVersion}, @@ -124,6 +126,7 @@ int RunWithCrashpadMain(int argc, char* argv[]) { std::map annotations; std::string database; std::string url; + std::string httpProxy; std::vector arguments; } options = {}; options.handler = "crashpad_handler"; @@ -158,6 +161,10 @@ int RunWithCrashpadMain(int argc, char* argv[]) { options.url = optarg; break; } + case kOptionHTTPProxy: { + options.httpProxy = optarg; + break; + } case kOptionArgument: { options.arguments.push_back(optarg); break; @@ -190,6 +197,7 @@ int RunWithCrashpadMain(int argc, char* argv[]) { base::FilePath(options.database), base::FilePath(), options.url, + options.httpProxy, options.annotations, options.arguments, false, From fb125169f513bbaf2a8e94a8d682b62f29330225 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 20:46:05 +0200 Subject: [PATCH 120/146] Make sure that iOS doesn't use the proxy. --- client/ios_handler/in_process_handler.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/ios_handler/in_process_handler.cc b/client/ios_handler/in_process_handler.cc index 668acc669f..dfdc03c9b7 100644 --- a/client/ios_handler/in_process_handler.cc +++ b/client/ios_handler/in_process_handler.cc @@ -92,7 +92,7 @@ bool InProcessHandler::Initialize( upload_thread_options.identify_client_via_url = true; upload_thread_.reset(new CrashReportUploadThread( - database_.get(), url, upload_thread_options, callback)); + database_.get(), url, "", upload_thread_options, callback)); } if (!CreateDirectory(database)) From f72392eddbebb4b48b7404707bc454f69d29505f Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 20:47:28 +0200 Subject: [PATCH 121/146] Fall back on deprecated NSURLConnection when not using an HTTP-proxy. --- util/net/http_transport_mac.mm | 155 +++++++++++++++++++++++---------- 1 file changed, 107 insertions(+), 48 deletions(-) diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index 9e796a247c..ec19730eb0 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -209,68 +209,90 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { ~HTTPTransportMac() override; bool ExecuteSynchronously(std::string* response_body) override; + + private: + static bool ExecuteNormalRequest(NSMutableURLRequest* request, + std::string* response_body); + static bool ExecuteProxyRequest(NSMutableURLRequest* request, + const std::string& proxy, + std::string* response_body); }; HTTPTransportMac::HTTPTransportMac() : HTTPTransport() {} -HTTPTransportMac::~HTTPTransportMac() {} - -bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) { - DCHECK(body_stream()); - - __block bool sync_rv = false; +HTTPTransportMac::~HTTPTransportMac() = default; +bool HTTPTransportMac::ExecuteNormalRequest(NSMutableURLRequest* request, + std::string* response_body) { @autoreleasepool { - NSString* url_ns_string = base::SysUTF8ToNSString(url()); - NSURL* url = [NSURL URLWithString:url_ns_string]; - NSMutableURLRequest* request = - [NSMutableURLRequest requestWithURL:url - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:timeout()]; - [request setHTTPMethod:base::SysUTF8ToNSString(method())]; - - // If left to its own devices, CFNetwork would build a user-agent string - // based on keys in the main bundle’s Info.plist, giving ugly results if - // there is no Info.plist. Provide a User-Agent string similar to the one - // that CFNetwork would use, but with appropriate values in place of the - // Info.plist-derived strings. - [request setValue:UserAgentString() forHTTPHeaderField:@"User-Agent"]; + NSURLResponse* response = nil; + NSError* error = nil; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // Deprecated in OS X 10.11. The suggested replacement, NSURLSession, is + // only available on 10.9 and later, and this needs to run on earlier + // releases. + NSData* body = [NSURLConnection sendSynchronousRequest:request + returningResponse:&response + error:&error]; +#pragma clang diagnostic pop + + if (error) { + Metrics::CrashUploadErrorCode(error.code); + LOG(ERROR) << [[error localizedDescription] UTF8String] << " (" + << [[error domain] UTF8String] << " " << [error code] << ")"; + return false; + } + if (!response) { + LOG(ERROR) << "no response"; + return false; + } + auto http_response = base::mac::ObjCCast(response); + if (!http_response) { + LOG(ERROR) << "no http_response"; + return false; + } + NSInteger http_status = [http_response statusCode]; + if (http_status < 200 || http_status > 203) { + LOG(ERROR) << base::StringPrintf("HTTP status %ld", + implicit_cast(http_status)); + return false; + } - for (const auto& pair : headers()) { - [request setValue:base::SysUTF8ToNSString(pair.second) - forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)]; + if (response_body) { + response_body->assign(static_cast([body bytes]), + [body length]); } - base::scoped_nsobject input_stream( - [[CrashpadHTTPBodyStreamTransport alloc] - initWithBodyStream:body_stream()]); - [request setHTTPBodyStream:input_stream.get()]; + return true; + } +} +bool HTTPTransportMac::ExecuteProxyRequest(NSMutableURLRequest* request, + const std::string& proxy, + std::string* response_body) { + __block bool sync_rv = false; + @autoreleasepool { NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration]; - if (!http_proxy().empty()) { - std::string proxy = http_proxy() + "/"; - std::string scheme, host, port, rest_ignored; - CrackURL(proxy, &scheme, &host, &port, &rest_ignored); - NSString* schemeNS = base::SysUTF8ToNSString(scheme); - NSString* hostNS = base::SysUTF8ToNSString(host); - NSNumber* proxy_port = @(std::stoi(port)); - - NSDictionary* proxyDict = @{ - (__bridge id)kCFNetworkProxiesHTTPEnable : @YES, - (__bridge id)kCFNetworkProxiesHTTPPort : proxy_port, - (__bridge id)kCFNetworkProxiesHTTPProxy : hostNS, - (__bridge id)@"HTTPSEnable" : @YES, - (__bridge id)@"HTTPSPort" : proxy_port, - (__bridge id)@"HTTPSProxy" : hostNS, - }; - sessionConfig.connectionProxyDictionary = proxyDict; - } - + std::string scheme, host, port, rest_ignored; + CrackURL(proxy, &scheme, &host, &port, &rest_ignored); + NSString* schemeNS = base::SysUTF8ToNSString(scheme); + NSString* hostNS = base::SysUTF8ToNSString(host); + NSNumber* proxy_port = @(std::stoi(port)); + + NSDictionary* proxyDict = @{ + (__bridge id)kCFNetworkProxiesHTTPEnable : @YES, + (__bridge id)kCFNetworkProxiesHTTPPort : proxy_port, + (__bridge id)kCFNetworkProxiesHTTPProxy : hostNS, + (__bridge id) @"HTTPSEnable" : @YES, + (__bridge id) @"HTTPSPort" : proxy_port, + (__bridge id) @"HTTPSProxy" : hostNS, + }; + sessionConfig.connectionProxyDictionary = proxyDict; NSURLSession* session = [NSURLSession sessionWithConfiguration:sessionConfig]; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); [[session dataTaskWithRequest:request completionHandler:^( @@ -317,10 +339,47 @@ - (BOOL)setProperty:(id)property forKey:(NSStreamPropertyKey)key { dispatch_semaphore_wait(semaphore, (dispatch_time_t)(10 * NSEC_PER_SEC)); } - return sync_rv; } +bool HTTPTransportMac::ExecuteSynchronously(std::string* response_body) { + DCHECK(body_stream()); + + @autoreleasepool { + NSString* url_ns_string = base::SysUTF8ToNSString(url()); + NSURL* url = [NSURL URLWithString:url_ns_string]; + NSMutableURLRequest* request = + [NSMutableURLRequest requestWithURL:url + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:timeout()]; + [request setHTTPMethod:base::SysUTF8ToNSString(method())]; + + // If left to its own devices, CFNetwork would build a user-agent string + // based on keys in the main bundle’s Info.plist, giving ugly results if + // there is no Info.plist. Provide a User-Agent string similar to the one + // that CFNetwork would use, but with appropriate values in place of the + // Info.plist-derived strings. + [request setValue:UserAgentString() forHTTPHeaderField:@"User-Agent"]; + + for (const auto& pair : headers()) { + [request setValue:base::SysUTF8ToNSString(pair.second) + forHTTPHeaderField:base::SysUTF8ToNSString(pair.first)]; + } + + base::scoped_nsobject input_stream( + [[CrashpadHTTPBodyStreamTransport alloc] + initWithBodyStream:body_stream()]); + [request setHTTPBodyStream:input_stream.get()]; + + if (http_proxy().empty()) { + ExecuteNormalRequest(request, response_body); + } else { + std::string proxy = http_proxy() + "/"; + ExecuteProxyRequest(request, proxy, response_body); + } + } +} + } // namespace // static From 7041349732770d0902e156deaba6e86bfdee1a0a Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 22 May 2023 20:57:34 +0200 Subject: [PATCH 122/146] Return the request success state --- util/net/http_transport_mac.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index ec19730eb0..48f3408a9e 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -372,10 +372,10 @@ static bool ExecuteProxyRequest(NSMutableURLRequest* request, [request setHTTPBodyStream:input_stream.get()]; if (http_proxy().empty()) { - ExecuteNormalRequest(request, response_body); + return ExecuteNormalRequest(request, response_body); } else { std::string proxy = http_proxy() + "/"; - ExecuteProxyRequest(request, proxy, response_body); + return ExecuteProxyRequest(request, proxy, response_body); } } } From 825cec152c940f07672e5708dcfb0685c00b958c Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 12 Jun 2023 13:11:45 +0200 Subject: [PATCH 123/146] feat: add response body to error log... ...in case the response status is not 200. This is important, because if the crashpad-handler receives an 400 (Bad Request) for whichever reason, it will detail why in the response-body. This helps customers and us to figure out why the minidump endpoint negates requests from the crashpad-handler in case this happens. --- util/net/http_transport_libcurl.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/net/http_transport_libcurl.cc b/util/net/http_transport_libcurl.cc index c8d8ee37cc..0c56c05ca7 100644 --- a/util/net/http_transport_libcurl.cc +++ b/util/net/http_transport_libcurl.cc @@ -483,7 +483,8 @@ bool HTTPTransportLibcurl::ExecuteSynchronously(std::string* response_body) { } if (status != 200) { - LOG(ERROR) << base::StringPrintf("HTTP status %ld", status); + LOG(ERROR) << base::StringPrintf( + "HTTP status %ld, response = \"%s\"", status, response_body->c_str()); return false; } From 6366a473b80d4aec00ee5bc876e821c35b9f446b Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 28 Sep 2023 17:06:04 +0200 Subject: [PATCH 124/146] Update mini_chromium --- third_party/mini_chromium/CMakeLists.txt | 27 ++++++++++++------------ third_party/mini_chromium/mini_chromium | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 4577c1d125..9b0de72c84 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -50,6 +50,8 @@ mc_append_sources( rand_util.h scoped_clear_last_error.h scoped_generic.h + strings/pattern.cc + strings/pattern.h strings/string_number_conversions.cc strings/string_number_conversions.h strings/string_piece.h @@ -85,21 +87,20 @@ 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 + apple/bridging.h + apple/foundation_util.h + apple/foundation_util.mm + apple/mach_logging.cc + apple/mach_logging.h + apple/scoped_cftyperef.h + apple/scoped_mach_port.cc + apple/scoped_mach_port.h + apple/scoped_mach_vm.cc + apple/scoped_mach_vm.h + apple/scoped_nsautorelease_pool.h + apple/scoped_nsautorelease_pool.mm strings/sys_string_conversions_mac.mm ) elseif(IOS) diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 4332ddb696..6a817b86d4 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 4332ddb6963750e1106efdcece6d6e2de6dc6430 +Subproject commit 6a817b86d4bd0e0672df4a094c4490ecef761a59 From a9f52498ac128edb6a0f39a8458d6eb05c0bb8a1 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 28 Sep 2023 17:07:23 +0200 Subject: [PATCH 125/146] Fix remaining base::mac reference in http_transport_mac --- util/net/http_transport_mac.mm | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index 102a0de858..342f0bd165 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -23,7 +23,6 @@ #include "base/strings/sys_string_conversions.h" #include "build/build_config.h" #include "package.h" -#include "util/file/file_io.h" #include "util/misc/implicit_cast.h" #include "util/misc/metrics.h" #include "util/net/http_body.h" @@ -248,8 +247,7 @@ static bool ExecuteProxyRequest(NSMutableURLRequest* request, LOG(ERROR) << "no response"; return false; } - NSHTTPURLResponse* http_response = - base::apple::ObjCCast(response); + auto http_response = base::apple::ObjCCast(response); if (!http_response) { LOG(ERROR) << "no http_response"; return false; @@ -315,7 +313,7 @@ static bool ExecuteProxyRequest(NSMutableURLRequest* request, return; } auto http_response = - base::mac::ObjCCast(response); + base::apple::ObjCCast(response); if (!http_response) { LOG(ERROR) << "no http_response"; sync_rv = false; @@ -369,7 +367,7 @@ static bool ExecuteProxyRequest(NSMutableURLRequest* request, } NSInputStream* input_stream = [[CrashpadHTTPBodyStreamTransport alloc] - initWithBodyStream:body_stream()]; + initWithBodyStream:body_stream()]; [request setHTTPBodyStream:input_stream]; if (http_proxy().empty()) { From 0e7a4f64430bb6c2821cbaf9bb4eca91b2ac0b28 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 28 Sep 2023 17:56:02 +0200 Subject: [PATCH 126/146] Use one FirstChanceHandler type per OS --- client/crashpad_client.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/crashpad_client.h b/client/crashpad_client.h index 91588780bb..fee602b454 100644 --- a/client/crashpad_client.h +++ b/client/crashpad_client.h @@ -443,7 +443,7 @@ class CrashpadClient { static void CrashWithoutDump(const std::string& message); //! \brief The type for custom handlers installed by clients. - using FirstChanceHandlerLinux = bool (*)(int, siginfo_t*, ucontext_t*); + using FirstChanceHandler = bool (*)(int, siginfo_t*, ucontext_t*); //! \brief Installs a custom crash signal handler which runs before the //! currently installed Crashpad handler. @@ -461,7 +461,7 @@ class CrashpadClient { //! signal handler is run. //! //! \param[in] handler The custom crash signal handler to install. - static void SetFirstChanceExceptionHandler(FirstChanceHandlerLinux handler); + static void SetFirstChanceExceptionHandler(FirstChanceHandler handler); //! \brief Installs a custom crash signal handler which runs after the //! currently installed Crashpad handler. @@ -678,7 +678,7 @@ class CrashpadClient { #if BUILDFLAG(IS_WIN) || DOXYGEN //! \brief The type for custom handlers installed by clients. - using FirstChanceHandlerWin = bool (*)(EXCEPTION_POINTERS*); + using FirstChanceHandler = bool (*)(EXCEPTION_POINTERS*); //! \brief Installs a custom unhandled exception filter which runs before the //! currently installed Crashpad handler. @@ -696,7 +696,7 @@ class CrashpadClient { //! unhandled exception handler is run. //! //! \param[in] handler The custom unhandled exception handler to install. - static void SetFirstChanceExceptionHandler(FirstChanceHandlerWin handler); + static void SetFirstChanceExceptionHandler(FirstChanceHandler handler); //! \brief Sets the IPC pipe of a presumably-running Crashpad handler process //! which was started with StartHandler() or by other compatible means From 695d159ff203e4dea32d31ef928cc4848911ddc1 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 29 Sep 2023 15:36:14 +0200 Subject: [PATCH 127/146] Fix compile-options for mini_chromium and crashpad_util on MSVC --- third_party/mini_chromium/CMakeLists.txt | 6 +++++- util/CMakeLists.txt | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 9b0de72c84..fb7023117d 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -182,7 +182,11 @@ target_link_libraries(mini_chromium if(WIN32) target_link_libraries(mini_chromium PRIVATE advapi32 kernel32) if(MSVC) - target_compile_options(mini_chromium PRIVATE "/wd4201" "/wd4996") + target_compile_options(mini_chromium PRIVATE + $<$:/wd4201> # nonstandard extension used : nameless struct/union. + $<$:/wd4244> # conversion from '__int64' to 'int32_t', possible loss of data. + $<$:/wd4996> # 'X' was declared deprecated. + ) target_compile_definitions(mini_chromium PRIVATE NOMINMAX UNICODE diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index ad676b793c..d3b1cf07f7 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -526,7 +526,9 @@ endif() if(WIN32) target_link_libraries(crashpad_util PRIVATE user32 version winhttp) if(MSVC) - target_compile_options(crashpad_util PRIVATE "/wd4201") + target_compile_options(crashpad_util PRIVATE + $<$:/wd4201> # nonstandard extension used : nameless struct/union. + ) if(CMAKE_SIZEOF_VOID_P EQUAL 4) set(CMAKE_ASM_MASM_FLAGS "${CMAKE_ASM_MASM_FLAGS} /safeseh") endif() From bd37fea5d70c3d052ae3b683c6102f036a71a18b Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 29 Sep 2023 16:23:43 +0200 Subject: [PATCH 128/146] Apply FirstChanceHandler changes on the Win side too --- client/crashpad_client_win.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index 03156606c1..895d9b7226 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -63,7 +63,7 @@ HANDLE g_signal_exception = INVALID_HANDLE_VALUE; // Where we store the exception information that the crash handler reads. ExceptionInformation g_crash_exception_information; -CrashpadClient::FirstChanceHandlerWin first_chance_handler_ = nullptr; +CrashpadClient::FirstChanceHandler first_chance_handler_ = nullptr; // Guards multiple simultaneous calls to DumpWithoutCrash() in the client. base::Lock* g_non_crash_dump_lock = nullptr; @@ -1148,7 +1148,7 @@ bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, // static void CrashpadClient::SetFirstChanceExceptionHandler( - FirstChanceHandlerWin handler) { + FirstChanceHandler handler) { first_chance_handler_ = handler; } From b601eb4b0754998925d8b3823189cb5666c939f1 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 29 Sep 2023 16:29:34 +0200 Subject: [PATCH 129/146] Update ios base build ref in mini_chromium --- third_party/mini_chromium/CMakeLists.txt | 27 ++++++++++++------------ 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index fb7023117d..14c98b4e2a 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -105,19 +105,20 @@ if(APPLE AND NOT IOS) ) 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 + apple/bridging.h + apple/foundation_util.h + apple/foundation_util.mm + apple/mach_logging.cc + apple/mach_logging.h + apple/scoped_cftyperef.h + apple/scoped_mach_port.cc + apple/scoped_mach_port.h + apple/scoped_mach_vm.cc + apple/scoped_mach_vm.h + apple/scoped_nsautorelease_pool.h + apple/scoped_nsautorelease_pool.mm + apple/scoped_nsobject.h + apple/scoped_typeref.h strings/sys_string_conversions_mac.mm ) endif() From 3299fb2874f7325020aa9a480f1aa237b8a06fb7 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Fri, 29 Sep 2023 16:32:21 +0200 Subject: [PATCH 130/146] Update ios base build ref in mini_chromium --- third_party/mini_chromium/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 14c98b4e2a..d94f9ed5a2 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -117,7 +117,6 @@ elseif(IOS) apple/scoped_mach_vm.h apple/scoped_nsautorelease_pool.h apple/scoped_nsautorelease_pool.mm - apple/scoped_nsobject.h apple/scoped_typeref.h strings/sys_string_conversions_mac.mm ) From dbe03a5272909b57dd750eaa75a238737498dc90 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Sat, 30 Sep 2023 10:11:23 +0200 Subject: [PATCH 131/146] Enable ARC in CMake build --- third_party/mini_chromium/CMakeLists.txt | 3 +++ util/net/http_transport_mac.mm | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index d94f9ed5a2..2fd9f83ce0 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -145,6 +145,9 @@ else() ) endif() +if(APPLE) + target_compile_options(mini_chromium PUBLIC -fobjc-arc -fno-objc-arc-exceptions) +endif() if(APPLE AND NOT IOS) target_link_libraries(mini_chromium PUBLIC "-framework ApplicationServices" diff --git a/util/net/http_transport_mac.mm b/util/net/http_transport_mac.mm index 342f0bd165..8b5ef49020 100644 --- a/util/net/http_transport_mac.mm +++ b/util/net/http_transport_mac.mm @@ -286,9 +286,9 @@ static bool ExecuteProxyRequest(NSMutableURLRequest* request, (__bridge id)kCFNetworkProxiesHTTPEnable : @YES, (__bridge id)kCFNetworkProxiesHTTPPort : proxy_port, (__bridge id)kCFNetworkProxiesHTTPProxy : hostNS, - (__bridge id) @"HTTPSEnable" : @YES, - (__bridge id) @"HTTPSPort" : proxy_port, - (__bridge id) @"HTTPSProxy" : hostNS, + @"HTTPSEnable" : @YES, + @"HTTPSPort" : proxy_port, + @"HTTPSProxy" : hostNS, }; sessionConfig.connectionProxyDictionary = proxyDict; NSURLSession* session = From f37da0f96ac60e919c0db6091d40000450ce7d6a Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 2 Oct 2023 10:56:34 +0200 Subject: [PATCH 132/146] Update lss to 9719c1e --- third_party/lss/lss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/lss/lss b/third_party/lss/lss index e1e7b0ad8e..9719c1e1e6 160000 --- a/third_party/lss/lss +++ b/third_party/lss/lss @@ -1 +1 @@ -Subproject commit e1e7b0ad8ee99a875b272c8e33e308472e897660 +Subproject commit 9719c1e1e676814c456b55f5f070eabad6709d31 From 45836a36a7f8acd5f6bdb51528f00a4bb3bd9590 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 12 Oct 2023 13:54:58 +0200 Subject: [PATCH 133/146] fix: replace DCHECK with LOG in minimdump_context_writer --- minidump/minidump_context_writer.cc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/minidump/minidump_context_writer.cc b/minidump/minidump_context_writer.cc index 84c0148203..26efc97a18 100644 --- a/minidump/minidump_context_writer.cc +++ b/minidump/minidump_context_writer.cc @@ -366,7 +366,36 @@ size_t MinidumpContextAMD64Writer::ContextSize() const { bool MinidumpXSaveAMD64CetU::InitializeFromSnapshot( const CPUContextX86_64* context_snapshot) { +#ifdef SENTRY_DISABLED DCHECK_EQ(context_snapshot->xstate.cet_u.cetmsr, 1ull); +#else + // TODO(supervacuus): this DCHECK led to multiple user inquiries because it + // ends up killing the crashpad_handler (when using a DEBUG build) which in + // turn keeps the crashpad client waiting indefinitely. + // + // It seems that crashpad devs put a DCHECK here because they already check + // at the call-site that the CET_U flag is enabled in the XSAVE feature set. + // However, that this flag is set, only means that the CET_U registers in + // XSAVE are valid, not necessarily that the SH_STK_EN bit is set. + // + // I couldn't find anything in the Intel (SDM 13.1) or AMD (PR 11.5.2, + // 18.11/12/13) CET/SS spec that would signal that SH_STK_EN(=cetmsr[0]) + // cannot be 0 at this point. Ideally, if SH_STK_EN is not set, then SSP + // should be set to 0 too (which means both are in their initial state). But + // even that should not lead to fatally exit the crashpad_handler (even in + // DEBUG), but rather produce a log and result in something that can be + // analysed in the backend. + // + // Any validation based on these register contents must check SH_STK_EN + // anyway or check SSP for !NULL and as a valid base like it is done here: + // https://chromium.googlesource.com/crashpad/crashpad/+/6278690abe6ef0dda047e67dc1d0c49ce7af3811/snapshot/win/thread_snapshot_win.cc#130 + if (!(context_snapshot->xstate.cet_u.cetmsr & 1ull)) { + LOG(WARNING) << "CET MSR enabled flag is not set (" + << context_snapshot->xstate.cet_u.cetmsr + << "); SSP = " + << context_snapshot->xstate.cet_u.ssp; + } +#endif cet_u_.cetmsr = context_snapshot->xstate.cet_u.cetmsr; cet_u_.ssp = context_snapshot->xstate.cet_u.ssp; return true; From 079636a07820e8e397745012201c87ac9bd51745 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Wed, 22 Nov 2023 15:08:08 -0800 Subject: [PATCH 134/146] build: adjust for Windows ARM64 Windows ARM64 uses a different ASM variant. The assembly is already correct but the CMake setup for the variant was not. This corrects that to enable building crashpad for Windows ARM64. Beyond this change, an update to zlib is required. --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88e14d2dc9..c0ea5b4b17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,11 @@ function(crashpad_install_dev) endfunction() if(WIN32) - enable_language(ASM_MASM) + if(CMAKE_SYSTEM_PROCESSOR MATCHES ARM64) + enable_language(ASM_MARMASM) + else() + enable_language(ASM_MASM) + endif() if(MINGW) find_program(JWASM_FOUND jwasm) From de6d596e4a5d808f92b0b58d476f4a563743308b Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 23 Nov 2023 14:36:31 +0100 Subject: [PATCH 135/146] Update zlib + mini_chromium in line with DEPS --- third_party/mini_chromium/mini_chromium | 2 +- third_party/zlib/zlib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/third_party/mini_chromium/mini_chromium b/third_party/mini_chromium/mini_chromium index 6a817b86d4..9e21183c1e 160000 --- a/third_party/mini_chromium/mini_chromium +++ b/third_party/mini_chromium/mini_chromium @@ -1 +1 @@ -Subproject commit 6a817b86d4bd0e0672df4a094c4490ecef761a59 +Subproject commit 9e21183c1ea369398d6f6ddd302c8db580bd19c4 diff --git a/third_party/zlib/zlib b/third_party/zlib/zlib index 13dc246a58..fef58692c1 160000 --- a/third_party/zlib/zlib +++ b/third_party/zlib/zlib @@ -1 +1 @@ -Subproject commit 13dc246a58e4b72104d35f9b1809af95221ebda7 +Subproject commit fef58692c1d7bec94c4ed3d030a45a1832a9615d From c6f83257af88683e7afabcba70036d0acb9172d9 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 23 Nov 2023 15:13:20 +0100 Subject: [PATCH 136/146] Update mini_chromium CMake to reflect upstream --- third_party/mini_chromium/CMakeLists.txt | 34 +++++++++--------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/third_party/mini_chromium/CMakeLists.txt b/third_party/mini_chromium/CMakeLists.txt index 2fd9f83ce0..48bfbe8df9 100644 --- a/third_party/mini_chromium/CMakeLists.txt +++ b/third_party/mini_chromium/CMakeLists.txt @@ -15,7 +15,7 @@ mc_append_sources( check.h check_op.h compiler_specific.h - cxx17_backports.h + containers/span.h debug/alias.cc debug/alias.h files/file_path.cc @@ -52,6 +52,9 @@ mc_append_sources( scoped_generic.h strings/pattern.cc strings/pattern.h + strings/strcat.cc + strings/strcat.h + strings/strcat_internal.h strings/string_number_conversions.cc strings/string_number_conversions.h strings/string_piece.h @@ -72,6 +75,7 @@ mc_append_sources( third_party/icu/icu_utf.h threading/thread_local_storage.cc threading/thread_local_storage.h + types/cxx23_to_underlying.h ) if(NOT MINGW) @@ -84,26 +88,7 @@ else() ) endif() -if(APPLE AND NOT IOS) - mc_append_sources( - mac/close_nocancel.cc - mac/scoped_ioobject.h - mac/scoped_launch_data.h - apple/bridging.h - apple/foundation_util.h - apple/foundation_util.mm - apple/mach_logging.cc - apple/mach_logging.h - apple/scoped_cftyperef.h - apple/scoped_mach_port.cc - apple/scoped_mach_port.h - apple/scoped_mach_vm.cc - apple/scoped_mach_vm.h - apple/scoped_nsautorelease_pool.h - apple/scoped_nsautorelease_pool.mm - strings/sys_string_conversions_mac.mm - ) -elseif(IOS) +if(APPLE) mc_append_sources( apple/bridging.h apple/foundation_util.h @@ -120,6 +105,13 @@ elseif(IOS) apple/scoped_typeref.h strings/sys_string_conversions_mac.mm ) + if (NOT IOS) + mc_append_sources( + mac/close_nocancel.cc + mac/scoped_ioobject.h + mac/scoped_launch_data.h + ) + endif() endif() if(WIN32) From 277df3f17ad3af89f91dabafb19f168b49f7e651 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 27 Nov 2023 11:11:45 +0100 Subject: [PATCH 137/146] Remove obsolete zlib sources --- third_party/zlib/CMakeLists.txt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 627b8352cb..0c7dc33740 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -27,11 +27,9 @@ else() zlib/inflate.h zlib/inftrees.c zlib/inftrees.h - zlib/names.h zlib/trees.c zlib/trees.h zlib/uncompr.c - zlib/x86.h zlib/zconf.h zlib/zlib.h zlib/zutil.c @@ -47,8 +45,6 @@ else() if(archs MATCHES "(x86_64)|(x86)|(i[3-7]86)|(AMD64)") target_sources(crashpad_zlib PRIVATE zlib/crc_folding.c - zlib/fill_window_sse.c - zlib/x86.c ) if(NOT MSVC) From fec839d5a43d484a7b66c787f7d2d446dd9db870 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Mon, 27 Nov 2023 11:19:51 +0100 Subject: [PATCH 138/146] include new check header in client-side stacktrace writer --- minidump/minidump_stacktrace_writer.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/minidump/minidump_stacktrace_writer.cc b/minidump/minidump_stacktrace_writer.cc index 74724ca773..10388c320a 100644 --- a/minidump/minidump_stacktrace_writer.cc +++ b/minidump/minidump_stacktrace_writer.cc @@ -6,6 +6,7 @@ #include #include +#include "base/check_op.h" #include "base/logging.h" #include "snapshot/exception_snapshot.h" #include "snapshot/thread_snapshot.h" From eb9a5e2d9da8fdfb1e8365fdc611a3916a82c275 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 28 Nov 2023 08:53:14 -0800 Subject: [PATCH 139/146] build: remove obsolete source file `simd_stub.c` was removed from the repository upstream. This updates the CMakeLists.txt so that it is possible to build for Windows ARM64. --- third_party/zlib/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/third_party/zlib/CMakeLists.txt b/third_party/zlib/CMakeLists.txt index 0c7dc33740..6d8a6eda11 100644 --- a/third_party/zlib/CMakeLists.txt +++ b/third_party/zlib/CMakeLists.txt @@ -50,8 +50,6 @@ else() if(NOT MSVC) target_compile_options(crashpad_zlib PRIVATE -msse4.2 -mpclmul) endif() - else() - target_sources(crashpad_zlib PRIVATE zlib/simd_stub.c) endif() target_compile_definitions(crashpad_zlib PUBLIC CRASHPAD_ZLIB_SOURCE_EMBEDDED From e8c474a4324b162f8e72f4af93014bf087651cd9 Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Tue, 28 Nov 2023 09:50:35 -0800 Subject: [PATCH 140/146] build: adjust process name matching The ARM64 build should use the ARM64 branch. The normal spelling for the CMAKE_SYSTEM_PROCESSOR is ARM64 when building with MSVC toolsets. This allows building for Windows ARM64 with MSBuild + CMake. --- util/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index d3b1cf07f7..0303d740c8 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -385,7 +385,7 @@ if(WIN32) win/traits.h win/xp_compat.h ) - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm") + if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm|ARM64") target_sources(crashpad_util PRIVATE misc/capture_context_win_arm64.asm ) From 89991e9910bc4c0893e45c8cfad0bdd31cc25a5c Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Wed, 6 Dec 2023 14:57:48 +0100 Subject: [PATCH 141/146] ci: add arm64 build (#94) --- .github/workflows/build.yml | 12 ++++++++++++ CMakeLists.txt | 2 +- cmake/toolchains/win_arm64.cmake | 6 ++++++ snapshot/win/process_reader_win.cc | 9 ++++++--- util/CMakeLists.txt | 2 +- 5 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 cmake/toolchains/win_arm64.cmake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ad02072f84..5161a689e9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,6 +34,18 @@ jobs: cmake -B cmake-build-stacks -D CRASHPAD_ENABLE_STACKTRACE=ON cmake --build cmake-build-stacks --parallel + - name: Build crashpad Windows ARM64 + if: ${{ runner.os == 'Windows' }} + run: | + cmake -B cmake-build-arm64 -DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/win_arm64.cmake" -DCRASHPAD_BUILD_TOOLS=On + cmake --build cmake-build-arm64 --config RelWithDebInfo -- /p:Platform=ARM64 + + - name: Build crashpad with client-side stack traces Windows ARM64 + if: ${{ runner.os == 'Windows' }} + run: | + cmake -B cmake-build-stacks-arm64 -DCMAKE_TOOLCHAIN_FILE="cmake/toolchains/win_arm64.cmake" -DCRASHPAD_ENABLE_STACKTRACE=ON + cmake --build cmake-build-stacks-arm64 --config RelWithDebInfo -- /p:Platform=ARM64 + build-ios: runs-on: macos-latest steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index c0ea5b4b17..79edcfafc2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,7 @@ function(crashpad_install_dev) endfunction() if(WIN32) - if(CMAKE_SYSTEM_PROCESSOR MATCHES ARM64) + if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES ARM64) enable_language(ASM_MARMASM) else() enable_language(ASM_MASM) diff --git a/cmake/toolchains/win_arm64.cmake b/cmake/toolchains/win_arm64.cmake new file mode 100644 index 0000000000..26ae4f386f --- /dev/null +++ b/cmake/toolchains/win_arm64.cmake @@ -0,0 +1,6 @@ +# Toolchain file that should provide required and non-conflicting build- +# parameters to allow normal and cross-compilation to ARM64 targets on +# Windows using any generator. +SET(CMAKE_GENERATOR_PLATFORM "ARM64") +SET(CMAKE_SYSTEM_PROCESSOR "ARM64") +SET(CMAKE_SYSTEM_NAME "Windows") \ No newline at end of file diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index 6650ac915e..8c3bb160db 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -174,9 +174,12 @@ void DoStackWalk(ProcessReaderWin::Thread* thread, // 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; +#elif defined(ARCH_CPU_ARM64) + machine_type = IMAGE_FILE_MACHINE_ARM64; + ctx = *thread->context.context(); + stack_frame.AddrPC.Offset = ctx.Pc; + stack_frame.AddrFrame.Offset = ctx.Fp; + stack_frame.AddrStack.Offset = ctx.Sp; #else #error Unsupported Windows Arch #endif // ARCH_CPU_X86 diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 0303d740c8..02ff865a49 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -385,7 +385,7 @@ if(WIN32) win/traits.h win/xp_compat.h ) - if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "arm|ARM64") + if("${CMAKE_SYSTEM_PROCESSOR}" MATCHES "arm|ARM64") target_sources(crashpad_util PRIVATE misc/capture_context_win_arm64.asm ) From 58d7207c3c5fac96bbbca8e760824714ec3f8a29 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Wed, 14 Feb 2024 10:46:59 +0100 Subject: [PATCH 142/146] build: remove obsolete CRASHPAD_WER_ENABLED (#96) --- CMakeLists.txt | 14 -------------- handler/CMakeLists.txt | 4 ++-- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 79edcfafc2..08a2d9d591 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -123,24 +123,10 @@ if(MSVC) $<$:/wd4577> # 'noexcept' used with no exception handling mode specified. $<$:/wd4996> # 'X' was declared deprecated. ) - - # WER support is only available starting from Win10 build 10941 - if("${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}" VERSION_LESS 10.0.19041) - message(STATUS "WER support disabled. Needs target platform >= 10.0.19041 (actual: ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})") - else() - SET(CRASHPAD_WER_ENABLED TRUE) - SET(CRASHPAD_WER_ENABLED TRUE PARENT_SCOPE) - message(STATUS "WER support enabled") - endif() elseif(MINGW) # redirect to wmain # FIXME: cmake 3.13 added target_link_options set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") - if(CRASHPAD_WER_ENABLED) - message(STATUS "WER support enabled") - else() - message(STATUS "WER support disabled. Define CRASHPAD_WER_ENABLED = TRUE to enable.") - endif() endif() add_library(crashpad::interface ALIAS crashpad_interface) diff --git a/handler/CMakeLists.txt b/handler/CMakeLists.txt index 0e5851c099..55d2e4a974 100644 --- a/handler/CMakeLists.txt +++ b/handler/CMakeLists.txt @@ -123,7 +123,7 @@ if(NOT IOS) ) endif() -if(CRASHPAD_WER_ENABLED) +if (WIN32) add_library(crashpad_wer SHARED win/wer/crashpad_wer.cc win/wer/crashpad_wer.h @@ -147,4 +147,4 @@ if(CRASHPAD_WER_ENABLED) install(TARGETS crashpad_wer EXPORT crashpad_export RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) -endif() +endif() \ No newline at end of file From 433ed68cd5d01c2934725f4614c1091512677646 Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Thu, 21 Mar 2024 17:27:21 +0100 Subject: [PATCH 143/146] update build-script for transport-compression changes in sentry-native (#97) --- CMakeLists.txt | 2 +- util/CMakeLists.txt | 13 +------------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 08a2d9d591..63cbd229e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ else() endif() option(CRASHPAD_ZLIB_SYSTEM "Use system zlib library" "${CRASHPAD_ZLIB_SYSTEM_DEFAULT}") -if(CRASHPAD_ZLIB_SYSTEM) +if(CRASHPAD_ZLIB_SYSTEM AND NOT ZLIB_FOUND) find_package(ZLIB REQUIRED) endif() diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 02ff865a49..b9161b4750 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -253,18 +253,7 @@ if(LINUX OR ANDROID) find_package(CURL REQUIRED) endif() - if(TARGET CURL::libcurl) # Only available in cmake 3.12+ - target_link_libraries(crashpad_util PRIVATE CURL::libcurl) - else() - # Needed for cmake < 3.12 support (cmake 3.12 introduced the target CURL::libcurl) - target_include_directories(crashpad_util 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(crashpad_util PRIVATE $) - target_compile_definitions(crashpad_util PRIVATE $) - endif() - + target_link_libraries(crashpad_util PRIVATE CURL::libcurl) SET(HTTP_TRANSPORT_IMPL net/http_transport_libcurl.cc) else() find_package(OpenSSL) From de1f5830ccc00dfa3f51611c021c9763af02b01b Mon Sep 17 00:00:00 2001 From: Mischan Toosarani-Hausberger Date: Tue, 2 Apr 2024 10:12:39 +0200 Subject: [PATCH 144/146] fix: send attachments without content-type (#98) --- util/net/http_multipart_builder.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/net/http_multipart_builder.cc b/util/net/http_multipart_builder.cc index 83514cb31b..26c297286f 100644 --- a/util/net/http_multipart_builder.cc +++ b/util/net/http_multipart_builder.cc @@ -169,10 +169,15 @@ std::unique_ptr HTTPMultipartBuilder::GetBodyStream() { for (const auto& pair : file_attachments_) { const FileAttachment& attachment = pair.second; std::string header = GetFormDataBoundary(boundary_, pair.first); +#ifdef SENTRY_MODIFIED header += base::StringPrintf("; filename=\"%s\"%s", attachment.filename.c_str(), kCRLF); header += base::StringPrintf("Content-Type: %s%s", attachment.content_type.c_str(), kBoundaryCRLF); +#else + header += base::StringPrintf("; filename=\"%s\"%s", + attachment.filename.c_str(), kBoundaryCRLF); +#endif streams.push_back(new StringHTTPBodyStream(header)); streams.push_back(new FileReaderHTTPBodyStream(attachment.reader)); From 96e301b7d6b81990a244d7de41a0d36eeb60899e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristj=C3=A1n=20Valur=20J=C3=B3nsson?= Date: Mon, 15 Apr 2024 07:48:16 +0000 Subject: [PATCH 145/146] Support Epic's "Easy Anti Cheat" (#99) --- client/crashpad_client_win.cc | 2 +- snapshot/win/process_reader_win.cc | 7 ++++-- util/win/exception_handler_server.cc | 35 ++++++++++++++++++++++++---- util/win/xp_compat.h | 4 ++++ 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/client/crashpad_client_win.cc b/client/crashpad_client_win.cc index fa87ef9969..cd1b206b0c 100644 --- a/client/crashpad_client_win.cc +++ b/client/crashpad_client_win.cc @@ -438,7 +438,7 @@ bool StartHandlerProcess( } ScopedKernelHANDLE this_process( - OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId())); + OpenProcess(kXPProcessLimitedAccess, true, GetCurrentProcessId())); if (!this_process.is_valid()) { PLOG(ERROR) << "OpenProcess"; return false; diff --git a/snapshot/win/process_reader_win.cc b/snapshot/win/process_reader_win.cc index 8c3bb160db..d203f6fd72 100644 --- a/snapshot/win/process_reader_win.cc +++ b/snapshot/win/process_reader_win.cc @@ -229,6 +229,7 @@ bool FillThreadContextAndSuspendCount(HANDLE process, thread->id == reinterpret_cast*>(NtCurrentTeb()) ->ClientId.UniqueThread; + bool did_suspend_thread = true; if (is_current_thread) { DCHECK(suspension_state == ProcessSuspensionState::kRunning); @@ -243,7 +244,9 @@ bool FillThreadContextAndSuspendCount(HANDLE process, DWORD previous_suspend_count = SuspendThread(thread_handle); if (previous_suspend_count == static_cast(-1)) { PLOG(ERROR) << "SuspendThread"; - return false; + // Must assume thread was already suspended, so we can still try to read + did_suspend_thread = false; + previous_suspend_count = 1; } if (previous_suspend_count <= 0 && suspension_state == ProcessSuspensionState::kSuspended) { @@ -281,7 +284,7 @@ bool FillThreadContextAndSuspendCount(HANDLE process, DoStackWalk(thread, process, thread_handle, is_64_reading_32); #endif - if (!ResumeThread(thread_handle)) { + if (did_suspend_thread && !ResumeThread(thread_handle)) { PLOG(ERROR) << "ResumeThread"; return false; } diff --git a/util/win/exception_handler_server.cc b/util/win/exception_handler_server.cc index e641c7fb94..32d767df7c 100644 --- a/util/win/exception_handler_server.cc +++ b/util/win/exception_handler_server.cc @@ -129,6 +129,7 @@ class ClientData { non_crash_dump_completed_event_( std::move(non_crash_dump_completed_event)), process_(std::move(process)), + process_promoted_(false), crash_exception_information_address_( crash_exception_information_address), non_crash_exception_information_address_( @@ -172,6 +173,29 @@ class ClientData { } HANDLE process() const { return process_.get(); } + // Promotes the process handle to full access if it hasn't already been done. + HANDLE process_promoted() + { + if (!process_promoted_) + { + // Duplicate restricted process handle for a full memory access handle. + HANDLE hAllAccessHandle = nullptr; + if (DuplicateHandle(GetCurrentProcess(), + process_.get(), + GetCurrentProcess(), + &hAllAccessHandle, + kXPProcessAllAccess, + FALSE, + 0)) + { + ScopedKernelHANDLE ScopedAllAccessHandle(hAllAccessHandle); + process_.swap(ScopedAllAccessHandle); + process_promoted_ = true; + } + } + return process_.get(); + } + private: void RegisterThreadPoolWaits( WAITORTIMERCALLBACK crash_dump_request_callback, @@ -232,6 +256,7 @@ class ClientData { ScopedKernelHANDLE non_crash_dump_requested_event_; ScopedKernelHANDLE non_crash_dump_completed_event_; ScopedKernelHANDLE process_; + bool process_promoted_; WinVMAddress crash_exception_information_address_; WinVMAddress non_crash_exception_information_address_; WinVMAddress debug_critical_section_address_; @@ -459,14 +484,14 @@ bool ExceptionHandlerServer::ServiceClientConnection( // the process, but the client will be able to, so we make a second attempt // having impersonated the client. HANDLE client_process = OpenProcess( - kXPProcessAllAccess, false, message.registration.client_process_id); + kXPProcessLimitedAccess, false, message.registration.client_process_id); if (!client_process) { if (!ImpersonateNamedPipeClient(service_context.pipe())) { PLOG(ERROR) << "ImpersonateNamedPipeClient"; return false; } client_process = OpenProcess( - kXPProcessAllAccess, false, message.registration.client_process_id); + kXPProcessLimitedAccess, false, message.registration.client_process_id); PCHECK(RevertToSelf()); if (!client_process) { LOG(ERROR) << "failed to open " << message.registration.client_process_id; @@ -543,11 +568,11 @@ void __stdcall ExceptionHandlerServer::OnCrashDumpEvent(void* ctx, BOOLEAN) { // Capture the exception. unsigned int exit_code = client->delegate()->ExceptionHandlerServerException( - client->process(), + client->process_promoted(), client->crash_exception_information_address(), client->debug_critical_section_address()); - SafeTerminateProcess(client->process(), exit_code); + SafeTerminateProcess(client->process_promoted(), exit_code); } // static @@ -558,7 +583,7 @@ void __stdcall ExceptionHandlerServer::OnNonCrashDumpEvent(void* ctx, BOOLEAN) { // Capture the exception. client->delegate()->ExceptionHandlerServerException( - client->process(), + client->process_promoted(), client->non_crash_exception_information_address(), client->debug_critical_section_address()); diff --git a/util/win/xp_compat.h b/util/win/xp_compat.h index 1499c08aa6..314fd73bb8 100644 --- a/util/win/xp_compat.h +++ b/util/win/xp_compat.h @@ -26,6 +26,10 @@ enum { //! against a Vista+ SDK results in `ERROR_ACCESS_DENIED` when running on XP. //! See https://msdn.microsoft.com/library/ms684880.aspx. kXPProcessAllAccess = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF, + + // A limited access version, suitable for initial access to the process. + kXPProcessLimitedAccess = PROCESS_DUP_HANDLE | PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_TERMINATE | SYNCHRONIZE, + //! \brief This is the XP-suitable value of `THREAD_ALL_ACCESS`. //! From a919765138253beabd1b36f60baa31e9c2ac129e Mon Sep 17 00:00:00 2001 From: jernejstrasner Date: Wed, 5 Jun 2024 11:27:06 +0200 Subject: [PATCH 146/146] Updated codeowners --- .github/CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7afbb99cb6..ad4ca06325 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1 +1 @@ -* @getsentry/owners-native +* @getsentry/processing