summaryrefslogtreecommitdiffstats
path: root/breakpad/linux
diff options
context:
space:
mode:
Diffstat (limited to 'breakpad/linux')
-rw-r--r--breakpad/linux/directory_reader.h105
-rw-r--r--breakpad/linux/directory_reader_unittest.cc77
-rw-r--r--breakpad/linux/exception_handler.cc308
-rw-r--r--breakpad/linux/exception_handler.h197
-rw-r--r--breakpad/linux/exception_handler_unittest.cc246
-rw-r--r--breakpad/linux/generate-test-dump.cc68
-rw-r--r--breakpad/linux/line_reader.h125
-rw-r--r--breakpad/linux/line_reader_unittest.cc184
-rw-r--r--breakpad/linux/linux_dumper.cc332
-rw-r--r--breakpad/linux/linux_dumper.h118
-rw-r--r--breakpad/linux/linux_dumper_unittest.cc66
-rw-r--r--breakpad/linux/linux_libc_support.h178
-rw-r--r--breakpad/linux/linux_libc_support_unittest.cc153
-rw-r--r--breakpad/linux/linux_syscall_support.h2800
-rw-r--r--breakpad/linux/memory.h176
-rw-r--r--breakpad/linux/memory_unittest.cc84
-rw-r--r--breakpad/linux/minidump-2-core.cc601
-rw-r--r--breakpad/linux/minidump_file_writer.cc254
-rw-r--r--breakpad/linux/minidump_format_linux.h44
-rw-r--r--breakpad/linux/minidump_writer.cc611
-rw-r--r--breakpad/linux/minidump_writer.h53
-rw-r--r--breakpad/linux/minidump_writer_unittest.cc70
22 files changed, 6850 insertions, 0 deletions
diff --git a/breakpad/linux/directory_reader.h b/breakpad/linux/directory_reader.h
new file mode 100644
index 0000000..76950ec
--- /dev/null
+++ b/breakpad/linux/directory_reader.h
@@ -0,0 +1,105 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_HANDLE_DIRECTORY_READER_H_
+#define CLIENT_LINUX_HANDLE_DIRECTORY_READER_H_
+
+#include <stdint.h>
+#include <unistd.h>
+#include <limits.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include "breakpad/linux/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// A class for enumerating a directory without using diropen/readdir or other
+// functions which may allocate memory.
+class DirectoryReader {
+ public:
+ DirectoryReader(int fd)
+ : fd_(fd),
+ buf_used_(0) {
+ }
+
+ // Return the next entry from the directory
+ // name: (output) the NUL terminated entry name
+ //
+ // Returns true iff successful (false on EOF).
+ //
+ // After calling this, one must call |PopEntry| otherwise you'll get the same
+ // entry over and over.
+ bool GetNextEntry(const char** name) {
+ struct kernel_dirent* const dent =
+ reinterpret_cast<kernel_dirent*>(buf_);
+
+ if (buf_used_ == 0) {
+ // need to read more entries.
+ const int n = sys_getdents(fd_, dent, sizeof(buf_));
+ if (n < 0) {
+ return false;
+ } else if (n == 0) {
+ hit_eof_ = true;
+ } else {
+ buf_used_ += n;
+ }
+ }
+
+ if (buf_used_ == 0 && hit_eof_)
+ return false;
+
+ assert(buf_used_ > 0);
+
+ *name = dent->d_name;
+ return true;
+ }
+
+ void PopEntry() {
+ if (!buf_used_)
+ return;
+
+ const struct kernel_dirent* const dent =
+ reinterpret_cast<kernel_dirent*>(buf_);
+
+ buf_used_ -= dent->d_reclen;
+ memmove(buf_, buf_ + dent->d_reclen, buf_used_);
+ }
+
+ private:
+ const int fd_;
+ bool hit_eof_;
+ unsigned buf_used_;
+ uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1];
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLE_DIRECTORY_READER_H_
diff --git a/breakpad/linux/directory_reader_unittest.cc b/breakpad/linux/directory_reader_unittest.cc
new file mode 100644
index 0000000..d04cc86
--- /dev/null
+++ b/breakpad/linux/directory_reader_unittest.cc
@@ -0,0 +1,77 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <set>
+#include <string>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "breakpad/linux/directory_reader.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test DirectoryReaderTest;
+}
+
+TEST(DirectoryReaderTest, CompareResults) {
+ std::set<std::string> dent_set;
+
+ DIR *const dir = opendir("/proc/self");
+ ASSERT_TRUE(dir != NULL);
+
+ struct dirent* dent;
+ while ((dent = readdir(dir)))
+ dent_set.insert(dent->d_name);
+
+ closedir(dir);
+
+ const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY);
+ ASSERT_GE(fd, 0);
+
+ DirectoryReader dir_reader(fd);
+ unsigned seen = 0;
+
+ const char* name;
+ while (dir_reader.GetNextEntry(&name)) {
+ ASSERT_TRUE(dent_set.find(name) != dent_set.end());
+ seen++;
+ dir_reader.PopEntry();
+ }
+
+ ASSERT_TRUE(dent_set.find("status") != dent_set.end());
+ ASSERT_TRUE(dent_set.find("stat") != dent_set.end());
+ ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end());
+
+ ASSERT_EQ(dent_set.size(), seen);
+ close(fd);
+}
diff --git a/breakpad/linux/exception_handler.cc b/breakpad/linux/exception_handler.cc
new file mode 100644
index 0000000..bbc6910
--- /dev/null
+++ b/breakpad/linux/exception_handler.cc
@@ -0,0 +1,308 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// The ExceptionHandler object installs signal handlers for a number of
+// signals. We rely on the signal handler running on the thread which crashed
+// in order to identify it. This is true of the synchronous signals (SEGV etc),
+// but not true of ABRT. Thus, if you send ABRT to yourself in a program which
+// uses ExceptionHandler, you need to use tgkill to direct it to the current
+// thread.
+//
+// The signal flow looks like this:
+//
+// SignalHandler (uses a global stack of ExceptionHandler objects to find
+// | one to handle the signal. If the first rejects it, try
+// | the second etc...)
+// V
+// HandleSignal ----------------------------| (clones a new process which
+// | | shares an address space with
+// (wait for cloned | the crashed process. This
+// process) | allows us to ptrace the crashed
+// | | process)
+// V V
+// (set signal handler to ThreadEntry (static function to bounce
+// SIG_DFL and rethrow, | back into the object)
+// killing the crashed |
+// process) V
+// DoDump (writes minidump)
+// |
+// V
+// sys_exit
+//
+
+// This code is a little fragmented. Different functions of the ExceptionHandler
+// class run in a number of different contexts. Some of them run in a normal
+// context and are easy to code, others run in a compromised context and the
+// restrictions at the top of minidump_writer.cc apply: no libc and use the
+// alternative malloc. Each function should have comment above it detailing the
+// context which it runs in.
+
+#include "breakpad/linux/exception_handler.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/signal.h>
+#include <sys/ucontext.h>
+#include <sys/user.h>
+#include <sys/syscall.h>
+#include <sys/wait.h>
+#include <sched.h>
+
+#include "breakpad/linux/linux_syscall_support.h"
+#include "breakpad/linux/memory.h"
+#include "breakpad/linux/minidump_writer.h"
+#include "common/linux/guid_creator.h"
+
+// A wrapper for the tgkill syscall: send a signal to a specific thread.
+static int tgkill(pid_t tgid, pid_t tid, int sig) {
+ syscall(__NR_tgkill, tgid, tid, sig);
+}
+
+namespace google_breakpad {
+
+// The list of signals which we consider to be crashes. The default action for
+// all these signals must be Core (see man 7 signal) because we rethrow the
+// signal after handling it and expect that it'll be fatal.
+static const int kExceptionSignals[] = {
+ SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1
+};
+
+// We can stack multiple exception handlers. In that case, this is the global
+// which holds the stack.
+std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
+unsigned ExceptionHandler::handler_stack_index_ = 0;
+pthread_mutex_t ExceptionHandler::handler_stack_mutex_ =
+ PTHREAD_MUTEX_INITIALIZER;
+
+// Runs before crashing: normal context.
+ExceptionHandler::ExceptionHandler(const std::string &dump_path,
+ FilterCallback filter,
+ MinidumpCallback callback,
+ void *callback_context,
+ bool install_handler)
+ : filter_(filter),
+ callback_(callback),
+ callback_context_(callback_context),
+ dump_path_(),
+ handler_installed_(install_handler),
+ crash_handler_(NULL) {
+ set_dump_path(dump_path);
+
+ if (install_handler) {
+ InstallHandlers();
+
+ pthread_mutex_lock(&handler_stack_mutex_);
+ if (handler_stack_ == NULL)
+ handler_stack_ = new std::vector<ExceptionHandler *>;
+ handler_stack_->push_back(this);
+ pthread_mutex_unlock(&handler_stack_mutex_);
+ }
+}
+
+// Runs before crashing: normal context.
+ExceptionHandler::~ExceptionHandler() {
+ UninstallHandlers();
+}
+
+// Runs before crashing: normal context.
+bool ExceptionHandler::InstallHandlers() {
+ // We run the signal handlers on an alternative stack because we might have
+ // crashed because of a stack overflow.
+
+ // We use this value rather than SIGSTKSZ because we would end up overrunning
+ // such a small stack.
+ static const unsigned kSigStackSize = 8192;
+
+ signal_stack = malloc(kSigStackSize);
+ stack_t stack;
+ memset(&stack, 0, sizeof(stack));
+ stack.ss_sp = signal_stack;
+ stack.ss_size = kSigStackSize;
+
+ if (sigaltstack(&stack, NULL) == -1)
+ return false;
+
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sigemptyset(&sa.sa_mask);
+
+ // mask all exception signals when we're handling one of them.
+ for (unsigned i = 0; kExceptionSignals[i] != -1; ++i)
+ sigaddset(&sa.sa_mask, kExceptionSignals[i]);
+
+ sa.sa_sigaction = SignalHandler;
+ sa.sa_flags = SA_ONSTACK | SA_SIGINFO;
+
+ for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) {
+ struct sigaction* old = new struct sigaction;
+ if (sigaction(kExceptionSignals[i], &sa, old) == -1)
+ return false;
+ old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old));
+ }
+}
+
+// Runs before crashing: normal context.
+void ExceptionHandler::UninstallHandlers() {
+ for (unsigned i = 0; i < old_handlers_.size(); ++i) {
+ struct sigaction *action =
+ reinterpret_cast<struct sigaction*>(old_handlers_[i].second);
+ sigaction(old_handlers_[i].first, action, NULL);
+ delete action;
+ }
+
+ old_handlers_.clear();
+}
+
+// Runs before crashing: normal context.
+void ExceptionHandler::UpdateNextID() {
+ GUID guid;
+ char guid_str[kGUIDStringLength + 1];
+ if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) {
+ next_minidump_id_ = guid_str;
+ next_minidump_id_c_ = next_minidump_id_.c_str();
+
+ char minidump_path[PATH_MAX];
+ snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp",
+ dump_path_c_,
+ guid_str);
+
+ next_minidump_path_ = minidump_path;
+ next_minidump_path_c_ = next_minidump_path_.c_str();
+ }
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the crashing thread.
+// static
+void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) {
+ // All the exception signals are blocked at this point.
+
+ pthread_mutex_lock(&handler_stack_mutex_);
+
+ if (!handler_stack_->size()) {
+ pthread_mutex_unlock(&handler_stack_mutex_);
+ return;
+ }
+
+ for (int i = handler_stack_->size() - 1; i >= 0; --i) {
+ if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) {
+ // successfully handled: We are in an invalid state since an exception
+ // signal has been delivered. We don't call the exit handlers because
+ // they could end up corrupting on-disk state.
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&handler_stack_mutex_);
+
+ // Terminate ourselves with the same signal so that our parent knows that we
+ // crashed. The default action for all the signals which we catch is Core, so
+ // this is the end of us.
+ signal(sig, SIG_DFL);
+ tgkill(getpid(), sys_gettid(), sig);
+
+ // not reached.
+}
+
+struct ThreadArgument {
+ pid_t pid; // the crashing process
+ ExceptionHandler* handler;
+ const void* context; // a CrashContext structure
+ size_t context_size;
+};
+
+// This is the entry function for the cloned process. We are in a compromised
+// context here: see the top of the file.
+// static
+int ExceptionHandler::ThreadEntry(void *arg) {
+ const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg);
+ return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context,
+ thread_arg->context_size) == false;
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the crashing thread.
+bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) {
+ if (filter_ && !filter_(callback_context_))
+ return false;
+
+ CrashContext context;
+ memcpy(&context.siginfo, info, sizeof(siginfo_t));
+ memcpy(&context.context, uc, sizeof(struct ucontext));
+ context.tid = sys_gettid();
+
+ if (crash_handler_ && crash_handler_(&context, sizeof(context),
+ callback_context_))
+ return true;
+
+ PageAllocator allocator;
+ void* const stack = allocator.Alloc(8000);
+
+ ThreadArgument thread_arg;
+ thread_arg.handler = this;
+ thread_arg.pid = getpid();
+ thread_arg.context = &context;
+ thread_arg.context_size = sizeof(context);
+
+ const pid_t child = sys_clone(
+ ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED,
+ &thread_arg, NULL, NULL, NULL);
+ int r, status;
+ do {
+ r = sys_waitpid(child, &status, __WALL);
+ } while (r == -1 && errno == EINTR);
+
+ if (r == -1) {
+ static const char msg[] = "ExceptionHandler::HandleSignal: waitpid failed:";
+ sys_write(2, msg, sizeof(msg) - 1);
+ sys_write(2, strerror(errno), strlen(strerror(errno)));
+ sys_write(2, "\n", 1);
+ }
+
+ bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0;
+
+ if (callback_)
+ success = callback_(dump_path_c_, next_minidump_id_c_,
+ callback_context_, success);
+
+ return success;
+}
+
+// This function runs in a compromised context: see the top of the file.
+// Runs on the cloned process.
+bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
+ size_t context_size) {
+ return google_breakpad::WriteMinidump(
+ next_minidump_path_c_, crashing_process, context, context_size);
+}
+
+} // namespace google_breakpad
diff --git a/breakpad/linux/exception_handler.h b/breakpad/linux/exception_handler.h
new file mode 100644
index 0000000..6176c94
--- /dev/null
+++ b/breakpad/linux/exception_handler.h
@@ -0,0 +1,197 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
+#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
+
+#include <vector>
+#include <string>
+
+#include <signal.h>
+
+namespace google_breakpad {
+
+// ExceptionHandler
+//
+// ExceptionHandler can write a minidump file when an exception occurs,
+// or when WriteMinidump() is called explicitly by your program.
+//
+// To have the exception handler write minidumps when an uncaught exception
+// (crash) occurs, you should create an instance early in the execution
+// of your program, and keep it around for the entire time you want to
+// have crash handling active (typically, until shutdown).
+// (NOTE): There should be only be one this kind of exception handler
+// object per process.
+//
+// If you want to write minidumps without installing the exception handler,
+// you can create an ExceptionHandler with install_handler set to false,
+// then call WriteMinidump. You can also use this technique if you want to
+// use different minidump callbacks for different call sites.
+//
+// In either case, a callback function is called when a minidump is written,
+// which receives the unqiue id of the minidump. The caller can use this
+// id to collect and write additional application state, and to launch an
+// external crash-reporting application.
+//
+// Caller should try to make the callbacks as crash-friendly as possible,
+// it should avoid use heap memory allocation as much as possible.
+class ExceptionHandler {
+ public:
+ // A callback function to run before Breakpad performs any substantial
+ // processing of an exception. A FilterCallback is called before writing
+ // a minidump. context is the parameter supplied by the user as
+ // callback_context when the handler was created.
+ //
+ // If a FilterCallback returns true, Breakpad will continue processing,
+ // attempting to write a minidump. If a FilterCallback returns false,
+ // Breakpad will immediately report the exception as unhandled without
+ // writing a minidump, allowing another handler the opportunity to handle it.
+ typedef bool (*FilterCallback)(void *context);
+
+ // A callback function to run after the minidump has been written.
+ // minidump_id is a unique id for the dump, so the minidump
+ // file is <dump_path>\<minidump_id>.dmp. context is the parameter supplied
+ // by the user as callback_context when the handler was created. succeeded
+ // indicates whether a minidump file was successfully written.
+ //
+ // If an exception occurred and the callback returns true, Breakpad will
+ // treat the exception as fully-handled, suppressing any other handlers from
+ // being notified of the exception. If the callback returns false, Breakpad
+ // will treat the exception as unhandled, and allow another handler to handle
+ // it. If there are no other handlers, Breakpad will report the exception to
+ // the system as unhandled, allowing a debugger or native crash dialog the
+ // opportunity to handle the exception. Most callback implementations
+ // should normally return the value of |succeeded|, or when they wish to
+ // not report an exception of handled, false. Callbacks will rarely want to
+ // return true directly (unless |succeeded| is true).
+ typedef bool (*MinidumpCallback)(const char *dump_path,
+ const char *minidump_id,
+ void *context,
+ bool succeeded);
+
+ // In certain cases, a user may wish to handle the generation of the minidump
+ // themselves. In this case, they can install a handler callback which is
+ // called when a crash has occured. If this function returns true, no other
+ // processing of occurs and the process will shortly be crashed. If this
+ // returns false, the normal processing continues.
+ typedef bool (*HandlerCallback)(const void* crash_context,
+ size_t crash_context_size,
+ void* context);
+
+ // Creates a new ExceptionHandler instance to handle writing minidumps.
+ // Before writing a minidump, the optional filter callback will be called.
+ // Its return value determines whether or not Breakpad should write a
+ // minidump. Minidump files will be written to dump_path, and the optional
+ // callback is called after writing the dump file, as described above.
+ // If install_handler is true, then a minidump will be written whenever
+ // an unhandled exception occurs. If it is false, minidumps will only
+ // be written when WriteMinidump is called.
+ ExceptionHandler(const std::string &dump_path,
+ FilterCallback filter, MinidumpCallback callback,
+ void *callback_context,
+ bool install_handler);
+ ~ExceptionHandler();
+
+ // Get and set the minidump path.
+ std::string dump_path() const { return dump_path_; }
+ void set_dump_path(const std::string &dump_path) {
+ dump_path_ = dump_path;
+ dump_path_c_ = dump_path_.c_str();
+ UpdateNextID();
+ }
+
+ void set_crash_handler(HandlerCallback callback) {
+ crash_handler_ = callback;
+ }
+
+ // Writes a minidump immediately. This can be used to capture the
+ // execution state independently of a crash. Returns true on success.
+ bool WriteMinidump();
+
+ // Convenience form of WriteMinidump which does not require an
+ // ExceptionHandler instance.
+ static bool WriteMinidump(const std::string &dump_path,
+ MinidumpCallback callback,
+ void *callback_context);
+
+ // This structure is passed to minidump_writer.h:WriteMinidump via an opaque
+ // blob. It shouldn't be needed in any user code.
+ struct CrashContext {
+ siginfo_t siginfo;
+ pid_t tid; // the crashing thread.
+ struct ucontext context;
+ };
+
+ private:
+ bool InstallHandlers();
+ void UninstallHandlers();
+ void PreresolveSymbols();
+
+ void UpdateNextID();
+ static void SignalHandler(int sig, siginfo_t* info, void* uc);
+ bool HandleSignal(int sig, siginfo_t* info, void* uc);
+ static int ThreadEntry(void* arg);
+ bool DoDump(pid_t crashing_process, const void* context,
+ size_t context_size);
+
+ const FilterCallback filter_;
+ const MinidumpCallback callback_;
+ void* const callback_context_;
+
+ std::string dump_path_;
+ std::string next_minidump_path_;
+ std::string next_minidump_id_;
+
+ // Pointers to C-string representations of the above. These are set
+ // when the above are set so we can avoid calling c_str during
+ // an exception.
+ const char* dump_path_c_;
+ const char* next_minidump_path_c_;
+ const char* next_minidump_id_c_;
+
+ const bool handler_installed_;
+ void* signal_stack; // the handler stack.
+ HandlerCallback crash_handler_;
+
+ // The global exception handler stack. This is need becuase there may exist
+ // multiple ExceptionHandler instances in a process. Each will have itself
+ // registered in this stack.
+ static std::vector<ExceptionHandler*> *handler_stack_;
+ // The index of the handler that should handle the next exception.
+ static unsigned handler_stack_index_;
+ static pthread_mutex_t handler_stack_mutex_;
+
+ // A vector of the old signal handlers. The void* is a pointer to a newly
+ // allocated sigaction structure to avoid pulling in too many includes.
+ std::vector<std::pair<int, void *> > old_handlers_;
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
diff --git a/breakpad/linux/exception_handler_unittest.cc b/breakpad/linux/exception_handler_unittest.cc
new file mode 100644
index 0000000..87eebf5
--- /dev/null
+++ b/breakpad/linux/exception_handler_unittest.cc
@@ -0,0 +1,246 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <string>
+
+#include <stdint.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include "base/eintr_wrapper.h"
+#include "breakpad/linux/exception_handler.h"
+#include "breakpad/linux/minidump_writer.h"
+#include "breakpad/linux/linux_libc_support.h"
+#include "breakpad/linux/linux_syscall_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace google_breakpad;
+
+static void sigchld_handler(int signo) { }
+
+class ExceptionHandlerTest : public ::testing::Test {
+ protected:
+ void SetUp() {
+ // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN.
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sigchld_handler;
+ ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1);
+ }
+
+ void TearDown() {
+ sigaction(SIGCHLD, &old_action, NULL);
+ }
+
+ struct sigaction old_action;
+};
+
+TEST(ExceptionHandlerTest, Simple) {
+ ExceptionHandler handler("/tmp", NULL, NULL, NULL, true);
+}
+
+static bool DoneCallback(const char* dump_path,
+ const char* minidump_id,
+ void* context,
+ bool succeeded) {
+ if (!succeeded)
+ return succeeded;
+
+ int fd = (int) context;
+ uint32_t len = my_strlen(minidump_id);
+ HANDLE_EINTR(sys_write(fd, &len, sizeof(len)));
+ HANDLE_EINTR(sys_write(fd, minidump_id, len));
+ sys_close(fd);
+
+ return true;
+}
+
+TEST(ExceptionHandlerTest, ChildCrash) {
+ int fds[2];
+ ASSERT_NE(pipe(fds), -1);
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[0]);
+ ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1],
+ true);
+ *reinterpret_cast<int*>(NULL) = 0;
+ }
+ close(fds[1]);
+
+ int status;
+ ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(WTERMSIG(status), SIGSEGV);
+
+ struct pollfd pfd;
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.fd = fds[0];
+ pfd.events = POLLIN | POLLERR;
+
+ const int r = HANDLE_EINTR(poll(&pfd, 1, 0));
+ ASSERT_EQ(r, 1);
+ ASSERT_TRUE(pfd.revents & POLLIN);
+
+ uint32_t len;
+ ASSERT_EQ(read(fds[0], &len, sizeof(len)), sizeof(len));
+ ASSERT_LT(len, 2048);
+ char* filename = reinterpret_cast<char*>(malloc(len + 1));
+ ASSERT_EQ(read(fds[0], filename, len), len);
+ filename[len] = 0;
+ close(fds[0]);
+
+ const std::string minidump_filename = std::string("/tmp/") + filename +
+ ".dmp";
+
+ struct stat st;
+ ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0);
+ ASSERT_GT(st.st_size, 0u);
+ unlink(minidump_filename.c_str());
+}
+
+static const unsigned kControlMsgSize =
+ CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred));
+
+static bool
+CrashHandler(const void* crash_context, size_t crash_context_size,
+ void* context) {
+ const int fd = (int) context;
+ int fds[2];
+ pipe(fds);
+
+ struct kernel_msghdr msg = {0};
+ struct kernel_iovec iov;
+ iov.iov_base = const_cast<void*>(crash_context);
+ iov.iov_len = crash_context_size;
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ char cmsg[kControlMsgSize];
+ memset(cmsg, 0, kControlMsgSize);
+ msg.msg_control = cmsg;
+ msg.msg_controllen = sizeof(cmsg);
+
+ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
+ hdr->cmsg_level = SOL_SOCKET;
+ hdr->cmsg_type = SCM_RIGHTS;
+ hdr->cmsg_len = CMSG_LEN(sizeof(int));
+ *((int*) CMSG_DATA(hdr)) = fds[1];
+ hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr);
+ hdr->cmsg_level = SOL_SOCKET;
+ hdr->cmsg_type = SCM_CREDENTIALS;
+ hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred));
+ struct ucred *cred = reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+ cred->uid = getuid();
+ cred->gid = getgid();
+ cred->pid = getpid();
+
+ HANDLE_EINTR(sys_sendmsg(fd, &msg, 0));
+ sys_close(fds[1]);
+
+ char b;
+ HANDLE_EINTR(sys_read(fds[0], &b, 1));
+
+ return true;
+}
+
+TEST(ExceptionHandlerTest, ExternalDumper) {
+ int fds[2];
+ ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1);
+ static const int on = 1;
+ setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+ setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[0]);
+ ExceptionHandler handler("/tmp", NULL, NULL, (void*) fds[1], true);
+ handler.set_crash_handler(CrashHandler);
+ *reinterpret_cast<int*>(NULL) = 0;
+ }
+
+ close(fds[1]);
+ struct msghdr msg = {0};
+ struct iovec iov;
+ static const unsigned kCrashContextSize =
+ sizeof(ExceptionHandler::CrashContext);
+ char context[kCrashContextSize];
+ char control[kControlMsgSize];
+ iov.iov_base = context;
+ iov.iov_len = kCrashContextSize;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = control;
+ msg.msg_controllen = kControlMsgSize;
+
+ const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0));
+ ASSERT_EQ(n, kCrashContextSize);
+ ASSERT_EQ(msg.msg_controllen, kControlMsgSize);
+ ASSERT_EQ(msg.msg_flags, 0);
+
+ pid_t crashing_pid = -1;
+ int signal_fd = -1;
+ for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr;
+ hdr = CMSG_NXTHDR(&msg, hdr)) {
+ if (hdr->cmsg_level != SOL_SOCKET)
+ continue;
+ if (hdr->cmsg_type == SCM_RIGHTS) {
+ const unsigned len = hdr->cmsg_len -
+ (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr);
+ ASSERT_EQ(len, sizeof(int));
+ signal_fd = *((int *) CMSG_DATA(hdr));
+ } else if (hdr->cmsg_type == SCM_CREDENTIALS) {
+ const struct ucred *cred =
+ reinterpret_cast<struct ucred*>(CMSG_DATA(hdr));
+ crashing_pid = cred->pid;
+ }
+ }
+
+ ASSERT_NE(crashing_pid, -1);
+ ASSERT_NE(signal_fd, -1);
+
+ char templ[] = "/tmp/exception-handler-unittest-XXXXXX";
+ mktemp(templ);
+ ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context, kCrashContextSize));
+ static const char b = 0;
+ HANDLE_EINTR(write(signal_fd, &b, 1));
+
+ int status;
+ ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1);
+ ASSERT_TRUE(WIFSIGNALED(status));
+ ASSERT_EQ(WTERMSIG(status), SIGSEGV);
+
+ struct stat st;
+ ASSERT_EQ(stat(templ, &st), 0);
+ ASSERT_GT(st.st_size, 0u);
+ unlink(templ);
+}
diff --git a/breakpad/linux/generate-test-dump.cc b/breakpad/linux/generate-test-dump.cc
new file mode 100644
index 0000000..ec668ed
--- /dev/null
+++ b/breakpad/linux/generate-test-dump.cc
@@ -0,0 +1,68 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This code exists to generate minidump files for testing.
+
+#include <stdlib.h>
+
+#include <unistd.h>
+
+#include "breakpad/linux/exception_handler.h"
+#include "breakpad/linux/linux_libc_support.h"
+#include "breakpad/linux/linux_syscall_support.h"
+
+static bool DumpCallback(const char* dump_path, const char* minidump_id,
+ void* context, bool success) {
+ if (!success) {
+ static const char msg[] = "Failed to write minidump\n";
+ sys_write(2, msg, sizeof(msg) - 1);
+ return false;
+ }
+
+ static const char msg[] = "Wrote minidump: ";
+ sys_write(2, msg, sizeof(msg) - 1);
+ sys_write(2, dump_path, my_strlen(dump_path));
+ sys_write(2, "/", 1);
+ sys_write(2, minidump_id, my_strlen(minidump_id));
+ sys_write(2, ".dmp\n", 5);
+
+ return true;
+}
+
+static void DoSomethingWhichCrashes() {
+ int local_var = 1;
+ *reinterpret_cast<char*>(NULL) = 1;
+}
+
+int main() {
+ google_breakpad::ExceptionHandler breakpad(".", NULL, DumpCallback, NULL,
+ true);
+ DoSomethingWhichCrashes();
+ return 0;
+}
diff --git a/breakpad/linux/line_reader.h b/breakpad/linux/line_reader.h
new file mode 100644
index 0000000..715e9e8
--- /dev/null
+++ b/breakpad/linux/line_reader.h
@@ -0,0 +1,125 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdint.h>
+#include <assert.h>
+#include <string.h>
+
+#include "breakpad/linux/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// A class for reading a file, line by line, without using fopen/fgets or other
+// functions which may allocate memory.
+class LineReader {
+ public:
+ LineReader(int fd)
+ : fd_(fd),
+ hit_eof_(false),
+ buf_used_(0) {
+ }
+
+ // The maximum length of a line.
+ static const size_t kMaxLineLen = 512;
+
+ // Return the next line from the file.
+ // line: (output) a pointer to the start of the line. The line is NUL
+ // terminated.
+ // len: (output) the length of the line (not inc the NUL byte)
+ //
+ // Returns true iff successful (false on EOF).
+ //
+ // One must call |PopLine| after this function, otherwise you'll continue to
+ // get the same line over and over.
+ bool GetNextLine(const char **line, unsigned *len) {
+ for (;;) {
+ if (buf_used_ == 0 && hit_eof_)
+ return false;
+
+ for (unsigned i = 0; i < buf_used_; ++i) {
+ if (buf_[i] == '\n' || buf_[i] == 0) {
+ buf_[i] = 0;
+ *len = i;
+ *line = buf_;
+ return true;
+ }
+ }
+
+ if (buf_used_ == sizeof(buf_)) {
+ // we scanned the whole buffer and didn't find an end-of-line marker.
+ // This line is too long to process.
+ return false;
+ }
+
+ // We didn't find any end-of-line terminators in the buffer. However, if
+ // this is the last line in the file it might not have one:
+ if (hit_eof_) {
+ assert(buf_used_);
+ // There's room for the NUL because of the buf_used_ == sizeof(buf_)
+ // check above.
+ buf_[buf_used_] = 0;
+ *len = buf_used_;
+ buf_used_ += 1; // since we appended the NUL.
+ *line = buf_;
+ return true;
+ }
+
+ // Otherwise, we should pull in more data from the file
+ const ssize_t n = sys_read(fd_, buf_ + buf_used_,
+ sizeof(buf_) - buf_used_);
+ if (n < 0) {
+ return false;
+ } else if (n == 0) {
+ hit_eof_ = true;
+ } else {
+ buf_used_ += n;
+ }
+
+ // At this point, we have either set the hit_eof_ flag, or we have more
+ // data to process...
+ }
+ }
+
+ void PopLine(unsigned len) {
+ // len doesn't include the NUL byte at the end.
+
+ assert(buf_used_ >= len + 1);
+ buf_used_ -= len + 1;
+ memmove(buf_, buf_ + len + 1, buf_used_);
+ }
+
+ private:
+ const int fd_;
+
+ bool hit_eof_;
+ unsigned buf_used_;
+ char buf_[kMaxLineLen];
+};
+
+} // namespace google_breakpad
diff --git a/breakpad/linux/line_reader_unittest.cc b/breakpad/linux/line_reader_unittest.cc
new file mode 100644
index 0000000..0f92764
--- /dev/null
+++ b/breakpad/linux/line_reader_unittest.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include "breakpad/linux/line_reader.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace google_breakpad;
+
+static int TemporaryFile() {
+ static const char templ[] = "/tmp/line-reader-unittest-XXXXXX";
+ char templ_copy[sizeof(templ)];
+ memcpy(templ_copy, templ, sizeof(templ));
+ const int fd = mkstemp(templ_copy);
+ if (fd >= 0)
+ unlink(templ_copy);
+
+ return fd;
+}
+
+namespace {
+typedef testing::Test LineReaderTest;
+}
+
+TEST(LineReaderTest, EmptyFile) {
+ const int fd = TemporaryFile();
+ LineReader reader(fd);
+
+ const char *line;
+ unsigned len;
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+ close(fd);
+}
+
+TEST(LineReaderTest, OneLineTerminated) {
+ const int fd = TemporaryFile();
+ write(fd, "a\n", 2);
+ lseek(fd, 0, SEEK_SET);
+ LineReader reader(fd);
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(len, 1);
+ ASSERT_EQ(line[0], 'a');
+ ASSERT_EQ(line[1], 0);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+ close(fd);
+}
+
+TEST(LineReaderTest, OneLine) {
+ const int fd = TemporaryFile();
+ write(fd, "a", 1);
+ lseek(fd, 0, SEEK_SET);
+ LineReader reader(fd);
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(len, 1);
+ ASSERT_EQ(line[0], 'a');
+ ASSERT_EQ(line[1], 0);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+ close(fd);
+}
+
+TEST(LineReaderTest, TwoLinesTerminated) {
+ const int fd = TemporaryFile();
+ write(fd, "a\nb\n", 4);
+ lseek(fd, 0, SEEK_SET);
+ LineReader reader(fd);
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(len, 1);
+ ASSERT_EQ(line[0], 'a');
+ ASSERT_EQ(line[1], 0);
+ reader.PopLine(len);
+
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(len, 1);
+ ASSERT_EQ(line[0], 'b');
+ ASSERT_EQ(line[1], 0);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+ close(fd);
+}
+
+TEST(LineReaderTest, TwoLines) {
+ const int fd = TemporaryFile();
+ write(fd, "a\nb", 3);
+ lseek(fd, 0, SEEK_SET);
+ LineReader reader(fd);
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(len, 1);
+ ASSERT_EQ(line[0], 'a');
+ ASSERT_EQ(line[1], 0);
+ reader.PopLine(len);
+
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(len, 1);
+ ASSERT_EQ(line[0], 'b');
+ ASSERT_EQ(line[1], 0);
+ reader.PopLine(len);
+
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+ close(fd);
+}
+
+TEST(LineReaderTest, MaxLength) {
+ const int fd = TemporaryFile();
+ char l[LineReader::kMaxLineLen - 1];
+ memset(l, 'a', sizeof(l));
+ write(fd, l, sizeof(l));
+ lseek(fd, 0, SEEK_SET);
+ LineReader reader(fd);
+
+ const char *line;
+ unsigned len;
+ ASSERT_TRUE(reader.GetNextLine(&line, &len));
+ ASSERT_EQ(len, sizeof(l));
+ ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0);
+ ASSERT_EQ(line[len], 0);
+
+ close(fd);
+}
+
+TEST(LineReaderTest, TooLong) {
+ const int fd = TemporaryFile();
+ char l[LineReader::kMaxLineLen];
+ memset(l, 'a', sizeof(l));
+ write(fd, l, sizeof(l));
+ lseek(fd, 0, SEEK_SET);
+ LineReader reader(fd);
+
+ const char *line;
+ unsigned len;
+ ASSERT_FALSE(reader.GetNextLine(&line, &len));
+
+ close(fd);
+}
diff --git a/breakpad/linux/linux_dumper.cc b/breakpad/linux/linux_dumper.cc
new file mode 100644
index 0000000..47465f2
--- /dev/null
+++ b/breakpad/linux/linux_dumper.cc
@@ -0,0 +1,332 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This code deals with the mechanics of getting information about a crashed
+// process. Since this code may run in a compromised address space, the same
+// rules apply as detailed at the top of minidump_writer.h: no libc calls and
+// use the alternative allocator.
+
+#include "breakpad/linux/linux_dumper.h"
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/types.h>
+#include <sys/ptrace.h>
+#include <sys/wait.h>
+
+#include "breakpad/linux/directory_reader.h"
+#include "breakpad/linux/line_reader.h"
+#include "breakpad/linux/linux_libc_support.h"
+#include "breakpad/linux/linux_syscall_support.h"
+
+// Suspend a thread by attaching to it.
+static bool SuspendThread(pid_t pid) {
+ // This may fail if the thread has just died or debugged.
+ errno = 0;
+ if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 &&
+ errno != 0) {
+ return false;
+ }
+ while (sys_waitpid(pid, NULL, __WALL) < 0) {
+ if (errno != EINTR) {
+ sys_ptrace(PTRACE_DETACH, pid, NULL, NULL);
+ return false;
+ }
+ }
+ return true;
+}
+
+// Resume a thread by detaching from it.
+static bool ResumeThread(pid_t pid) {
+ return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0;
+}
+
+namespace google_breakpad {
+
+LinuxDumper::LinuxDumper(int pid)
+ : pid_(pid),
+ threads_suspened_(false),
+ threads_(&allocator_, 8),
+ mappings_(&allocator_) {
+}
+
+bool LinuxDumper::Init() {
+ return EnumerateThreads(&threads_) &&
+ EnumerateMappings(&mappings_);
+}
+
+bool LinuxDumper::ThreadsSuspend() {
+ if (threads_suspened_)
+ return true;
+ bool good = true;
+ for (size_t i = 0; i < threads_.size(); ++i)
+ good &= SuspendThread(threads_[i]);
+ threads_suspened_ = true;
+ return good;
+}
+
+bool LinuxDumper::ThreadsResume() {
+ if (!threads_suspened_)
+ return false;
+ bool good = true;
+ for (size_t i = 0; i < threads_.size(); ++i)
+ good &= ResumeThread(threads_[i]);
+ threads_suspened_ = false;
+ return good;
+}
+
+bool
+LinuxDumper::EnumerateMappings(wasteful_vector<MappingInfo*> *result) const {
+ char maps_path[80];
+ memcpy(maps_path, "/proc/", 6);
+ const unsigned pid_len = my_int_len(pid_);
+ my_itos(maps_path + 6, pid_, pid_len);
+ memcpy(maps_path + 6 + pid_len, "/maps", 6);
+
+ const int fd = sys_open(maps_path, O_RDONLY, 0);
+ if (fd < 0)
+ return false;
+ LineReader *const line_reader = new(allocator_) LineReader(fd);
+
+ const char *line;
+ unsigned line_len;
+ while (line_reader->GetNextLine(&line, &line_len)) {
+ uintptr_t start_addr;
+ uintptr_t end_addr;
+
+ const char* i1 = my_read_hex_ptr(&start_addr, line);
+ if (*i1 == '-') {
+ const char *i2 = my_read_hex_ptr(&end_addr, i1 + 1);
+ if (*i2 == ' ') {
+ MappingInfo *const module = new(allocator_) MappingInfo;
+ memset(module, 0, sizeof(MappingInfo));
+ module->start_addr = start_addr;
+ module->size = end_addr - start_addr;
+ const char *name = NULL;
+ // Only copy name if the name is a valid path name.
+ if ((name = my_strchr(line, '/')) != NULL) {
+ const unsigned l = my_strlen(name);
+ if (l < sizeof(module->name))
+ memcpy(module->name, name, l);
+ }
+
+ result->push_back(module);
+ }
+ }
+
+ line_reader->PopLine(line_len);
+ }
+
+ sys_close(fd);
+ return result->size() > 0;
+}
+
+// Parse /proc/$pid/task to list all the threads of the process identified by
+// pid.
+bool LinuxDumper::EnumerateThreads(wasteful_vector<pid_t> *result) const {
+ char task_path[80];
+ memcpy(task_path, "/proc/", 6);
+ const unsigned pid_len = my_int_len(pid_);
+ my_itos(task_path + 6, pid_, pid_len);
+ memcpy(task_path + 6 + pid_len, "/task", 6);
+
+ const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0);
+ if (fd < 0)
+ return false;
+ DirectoryReader *dir_reader = new(allocator_) DirectoryReader(fd);
+
+ // The directory may contain duplicate entries which we filter by assuming
+ // that they are consecutive
+ int last_tid = -1;
+ const char *dent_name;
+ while (dir_reader->GetNextEntry(&dent_name)) {
+ if (my_strcmp(dent_name, ".") &&
+ my_strcmp(dent_name, "..")) {
+ int tid = 0;
+ if (my_strtoui(&tid, dent_name) &&
+ last_tid != tid) {
+ last_tid = tid;
+ result->push_back(tid);
+ }
+ }
+ dir_reader->PopEntry();
+ }
+
+ sys_close(fd);
+ return true;
+}
+
+// Read thread info from /proc/$pid/status.
+// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailible,
+// these members are set to -1. Returns true iff all three members are
+// availible.
+bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo *info) {
+ assert(info != NULL);
+ char status_path[80];
+ memcpy(status_path, "/proc/", 6);
+ const unsigned tid_len = my_int_len(tid);
+ my_itos(status_path + 6, tid, tid_len);
+ memcpy(status_path + 6 + tid_len, "/status", 8);
+ const int fd = open(status_path, O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ LineReader *const line_reader = new(allocator_) LineReader(fd);
+ const char *line;
+ unsigned line_len;
+
+ info->ppid = info->tgid = -1;
+
+ while (line_reader->GetNextLine(&line, &line_len)) {
+ if (my_strncmp("Tgid:\t", line, 6) == 0) {
+ my_strtoui(&info->tgid, line + 6);
+ } else if (my_strncmp("PPid:\t", line, 6) == 0) {
+ my_strtoui(&info->ppid, line + 6);
+ }
+
+ line_reader->PopLine(line_len);
+ }
+
+ if (info->ppid == -1 || info->tgid == -1)
+ return false;
+
+ if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 ||
+ sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) {
+ return false;
+ }
+
+#if defined(__i386) || defined(__x86_64)
+ if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1)
+ return false;
+
+ for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) {
+ if (sys_ptrace(
+ PTRACE_PEEKUSER, tid,
+ (void *) (offsetof(struct user,
+ u_debugreg[0]) + i * sizeof(debugreg_t)),
+ &info->dregs[i]) == -1) {
+ return false;
+ }
+ }
+#endif
+
+ const uint8_t *stack_pointer;
+#if defined(__i386)
+ memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp));
+#elif defined(__x86_64)
+ memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp));
+#else
+#error "This code hasn't been ported to your platform yet."
+#endif
+
+ if (!GetStackInfo(&info->stack, &info->stack_len,
+ (uintptr_t) stack_pointer))
+ return false;
+
+ return true;
+}
+
+// Get information about the stack, given the stack pointer. We don't try to
+// walk the stack since we might not have all the information needed to do
+// unwind. So we just grab, up to, 32k of stack.
+bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len,
+ uintptr_t int_stack_pointer) {
+#if defined(__i386) || defined(__x86_64)
+ static const bool stack_grows_down = true;
+ static const uintptr_t page_size = 4096;
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+ // Move the stack pointer to the bottom of the page that it's in.
+ uint8_t* const stack_pointer =
+ (uint8_t *) (int_stack_pointer & ~(page_size - 1));
+
+ // The number of bytes of stack which we try to capture.
+ static unsigned kStackToCapture = 32 * 1024;
+
+ const MappingInfo *mapping = FindMapping(stack_pointer);
+ if (!mapping)
+ return false;
+ if (stack_grows_down) {
+ const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
+ const ptrdiff_t distance_to_end =
+ static_cast<ptrdiff_t>(mapping->size) - offset;
+ *stack_len = distance_to_end > kStackToCapture ?
+ kStackToCapture : distance_to_end;
+ *stack = stack_pointer;
+ } else {
+ const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr;
+ *stack_len = offset > kStackToCapture ? kStackToCapture : offset;
+ *stack = stack_pointer - *stack_len;
+ }
+
+ return true;
+}
+
+// static
+void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src,
+ size_t length) {
+ unsigned long tmp;
+ size_t done = 0;
+ static const size_t word_size = sizeof(tmp);
+ uint8_t* const local = (uint8_t*) dest;
+ uint8_t* const remote = (uint8_t*) src;
+
+ while (done < length) {
+ const size_t l = length - done > word_size ? word_size : length - done;
+ if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1)
+ tmp = 0;
+ memcpy(local + done, &tmp, l);
+ done += l;
+ }
+}
+
+// Find the mapping which the given memory address falls in.
+const MappingInfo *LinuxDumper::FindMapping(const void *address) const {
+ const uintptr_t addr = (uintptr_t) address;
+
+ for (size_t i = 0; i < mappings_.size(); ++i) {
+ const uintptr_t start = static_cast<uintptr_t>(mappings_[i]->start_addr);
+ if (addr >= start && addr - start < mappings_[i]->size)
+ return mappings_[i];
+ }
+
+ return NULL;
+}
+
+} // namespace google_breakpad
diff --git a/breakpad/linux/linux_dumper.h b/breakpad/linux/linux_dumper.h
new file mode 100644
index 0000000..1e39a6e
--- /dev/null
+++ b/breakpad/linux/linux_dumper.h
@@ -0,0 +1,118 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
+#define CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
+
+#include <stdint.h>
+#include <sys/user.h>
+#include <linux/limits.h>
+
+#include "breakpad/linux/memory.h"
+
+namespace google_breakpad {
+
+typedef typeof(((struct user *) 0)->u_debugreg[0]) debugreg_t;
+
+// We produce one of these structures for each thread in the crashed process.
+struct ThreadInfo {
+ pid_t tgid; // thread group id
+ pid_t ppid; // parent process
+
+ // Even on platforms where the stack grows down, the following will point to
+ // the smallest address in the stack.
+ const void *stack; // pointer to the stack area
+ size_t stack_len; // length of the stack to copy
+
+ user_regs_struct regs;
+ user_fpregs_struct fpregs;
+#if defined(__i386) || defined(__x86_64)
+ user_fpxregs_struct fpxregs;
+
+ static const unsigned kNumDebugRegisters = 8;
+ debugreg_t dregs[8];
+#endif
+};
+
+// One of these is produced for each mapping in the process (i.e. line in
+// /proc/$x/maps).
+struct MappingInfo {
+ uintptr_t start_addr;
+ size_t size;
+ char name[NAME_MAX];
+};
+
+class LinuxDumper {
+ public:
+ explicit LinuxDumper(pid_t pid);
+
+ // Parse the data for |threads| and |mappings|.
+ bool Init();
+
+ // Suspend/resume all threads in the given process.
+ bool ThreadsSuspend();
+ bool ThreadsResume();
+
+ // Read information about the given thread. Returns true on success. One must
+ // have called |ThreadsSuspend| first.
+ bool ThreadInfoGet(pid_t tid, ThreadInfo *info);
+
+ // These are only valid after a call to |Init|.
+ const wasteful_vector<pid_t> &threads() { return threads_; }
+ const wasteful_vector<MappingInfo*> &mappings() { return mappings_; }
+ const MappingInfo *FindMapping(const void *address) const;
+
+ // Find a block of memory to take as the stack given the top of stack pointer.
+ // stack: (output) the lowest address in the memory area
+ // stack_len: (output) the length of the memory area
+ // stack_top: the current top of the stack
+ bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top);
+
+ PageAllocator *allocator() { return &allocator_; }
+
+ // memcpy from a remote process.
+ static void CopyFromProcess(void* dest, pid_t child, const void* src,
+ size_t length);
+
+ private:
+ bool EnumerateMappings(wasteful_vector<MappingInfo*> *result) const;
+ bool EnumerateThreads(wasteful_vector<pid_t> *result) const;
+
+ const pid_t pid_;
+
+ mutable PageAllocator allocator_;
+
+ bool threads_suspened_;
+ wasteful_vector<pid_t> threads_; // the ids of all the threads
+ wasteful_vector<MappingInfo*> mappings_; // info from /proc/<pid>/maps
+};
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_
diff --git a/breakpad/linux/linux_dumper_unittest.cc b/breakpad/linux/linux_dumper_unittest.cc
new file mode 100644
index 0000000..692c696
--- /dev/null
+++ b/breakpad/linux/linux_dumper_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <unistd.h>
+
+#include "breakpad/linux/linux_dumper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test LinuxDumperTest;
+}
+
+TEST(LinuxDumperTest, Setup) {
+ LinuxDumper dumper(getpid());
+}
+
+TEST(LinuxDumperTest, FindMappings) {
+ LinuxDumper dumper(getpid());
+ ASSERT_TRUE(dumper.Init());
+
+ ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid)));
+ ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf)));
+ ASSERT_FALSE(dumper.FindMapping(NULL));
+}
+
+TEST(LinuxDumperTest, ThreadList) {
+ LinuxDumper dumper(getpid());
+ ASSERT_TRUE(dumper.Init());
+
+ ASSERT_GE(dumper.threads().size(), 1);
+ bool found = false;
+ for (size_t i = 0; i < dumper.threads().size(); ++i) {
+ if (dumper.threads()[i] == getpid()) {
+ found = true;
+ break;
+ }
+ }
+}
diff --git a/breakpad/linux/linux_libc_support.h b/breakpad/linux/linux_libc_support.h
new file mode 100644
index 0000000..e08f27f
--- /dev/null
+++ b/breakpad/linux/linux_libc_support.h
@@ -0,0 +1,178 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This header provides replacements for libc functions that we need. We if
+// call the libc functions directly we risk crashing in the dynamic linker as
+// it tries to resolve uncached PLT entries.
+
+#ifndef CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_
+#define CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_
+
+#include <stdint.h>
+#include <limits.h>
+#include <sys/types.h>
+
+extern "C" {
+
+static inline size_t
+my_strlen(const char* s) {
+ size_t len = 0;
+ while (*s++) len++;
+ return len;
+}
+
+static inline int
+my_strcmp(const char* a, const char* b) {
+ for (;;) {
+ if (*a < *b)
+ return -1;
+ else if (*a > *b)
+ return 1;
+ else if (*a == 0)
+ return 0;
+ a++;
+ b++;
+ }
+}
+
+static inline int
+my_strncmp(const char* a, const char* b, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ if (*a < *b)
+ return -1;
+ else if (*a > *b)
+ return 1;
+ else if (*a == 0)
+ return 0;
+ a++;
+ b++;
+ }
+
+ return 0;
+}
+
+// Parse a non-negative integer.
+// result: (output) the resulting non-negative integer
+// s: a NUL terminated string
+// Return true iff successful.
+static inline bool
+my_strtoui(int* result, const char* s) {
+ if (*s == 0)
+ return false;
+ int r = 0;
+ for (;; s++) {
+ if (*s == 0)
+ break;
+ const int old_r = r;
+ r *= 10;
+ if (*s < '0' || *s > '9')
+ return false;
+ r += *s - '0';
+ if (r < old_r)
+ return false;
+ }
+
+ *result = r;
+ return true;
+}
+
+// Return the length of the given, non-negative integer when expressed in base
+// 10.
+static inline unsigned
+my_int_len(int i) {
+ if (!i)
+ return 1;
+
+ int len = 0;
+ while (i) {
+ len++;
+ i /= 10;
+ }
+
+ return len;
+}
+
+// Convert a non-negative integer to a string
+// output: (output) the resulting string is written here. This buffer must be
+// large enough to hold the resulting string. Call |my_int_len| to get the
+// required length.
+// i: the non-negative integer to serialise.
+// i_len: the length of the integer in base 10 (see |my_int_len|).
+static inline void
+my_itos(char* output, int i, unsigned i_len) {
+ for (unsigned index = i_len; index; --index, i /= 10)
+ output[index - 1] = '0' + (i % 10);
+}
+
+static inline const char*
+my_strchr(const char* haystack, char needle) {
+ while (*haystack && *haystack != needle)
+ haystack++;
+ if (*haystack == needle)
+ return haystack;
+ return (const char*) 0;
+}
+
+// Read a hex value
+// result: (output) the resulting value
+// s: a string
+// Returns a pointer to the first invalid charactor.
+static inline const char*
+my_read_hex_ptr(uintptr_t* result, const char* s) {
+ uintptr_t r = 0;
+
+ for (;; ++s) {
+ if (*s >= '0' && *s <= '9') {
+ r <<= 4;
+ r += *s - '0';
+ } else if (*s >= 'a' && *s <= 'f') {
+ r <<= 4;
+ r += (*s - 'a') + 10;
+ } else if (*s >= 'A' && *s <= 'F') {
+ r <<= 4;
+ r += (*s - 'A') + 10;
+ } else {
+ break;
+ }
+ }
+
+ *result = r;
+ return s;
+}
+
+static inline void
+my_memset(void* ip, char c, size_t len) {
+ char* p = (char *) ip;
+ while (len--)
+ *p++ = c;
+}
+
+} // extern "C"
+
+#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_
diff --git a/breakpad/linux/linux_libc_support_unittest.cc b/breakpad/linux/linux_libc_support_unittest.cc
new file mode 100644
index 0000000..3b5d611
--- /dev/null
+++ b/breakpad/linux/linux_libc_support_unittest.cc
@@ -0,0 +1,153 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "breakpad/linux/linux_libc_support.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+typedef testing::Test LinuxLibcSupportTest;
+}
+
+TEST(LinuxLibcSupportTest, strlen) {
+ static const char* test_data[] = { "", "a", "aa", "aaa", "aabc", NULL };
+ for (unsigned i = 0; ; ++i) {
+ if (!test_data[i])
+ break;
+ ASSERT_EQ(strlen(test_data[i]), my_strlen(test_data[i]));
+ }
+}
+
+TEST(LinuxLibcSupportTest, strcmp) {
+ static const char* test_data[] = {
+ "", "",
+ "a", "",
+ "", "a",
+ "a", "b",
+ "a", "a",
+ "ab", "aa",
+ "abc", "ab",
+ "abc", "abc",
+ NULL,
+ };
+
+ for (unsigned i = 0; ; ++i) {
+ if (!test_data[i*2])
+ break;
+ ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]),
+ strcmp(test_data[i*2], test_data[i*2 + 1]));
+ }
+}
+
+TEST(LinuxLibcSupportTest, strtoui) {
+ int result;
+
+ ASSERT_FALSE(my_strtoui(&result, ""));
+ ASSERT_FALSE(my_strtoui(&result, "-1"));
+ ASSERT_FALSE(my_strtoui(&result, "-"));
+ ASSERT_FALSE(my_strtoui(&result, "a"));
+ ASSERT_FALSE(my_strtoui(&result, "23472893472938472987987398472398"));
+
+ ASSERT_TRUE(my_strtoui(&result, "0"));
+ ASSERT_EQ(result, 0);
+ ASSERT_TRUE(my_strtoui(&result, "1"));
+ ASSERT_EQ(result, 1);
+ ASSERT_TRUE(my_strtoui(&result, "12"));
+ ASSERT_EQ(result, 12);
+ ASSERT_TRUE(my_strtoui(&result, "123"));
+ ASSERT_EQ(result, 123);
+ ASSERT_TRUE(my_strtoui(&result, "0123"));
+ ASSERT_EQ(result, 123);
+}
+
+TEST(LinuxLibcSupportTest, int_len) {
+ ASSERT_EQ(my_int_len(0), 1);
+ ASSERT_EQ(my_int_len(2), 1);
+ ASSERT_EQ(my_int_len(5), 1);
+ ASSERT_EQ(my_int_len(9), 1);
+ ASSERT_EQ(my_int_len(10), 2);
+ ASSERT_EQ(my_int_len(99), 2);
+ ASSERT_EQ(my_int_len(100), 3);
+ ASSERT_EQ(my_int_len(101), 3);
+ ASSERT_EQ(my_int_len(1000), 4);
+}
+
+TEST(LinuxLibcSupportTest, itos) {
+ char buf[10];
+
+ my_itos(buf, 0, 1);
+ ASSERT_EQ(0, memcmp(buf, "0", 1));
+
+ my_itos(buf, 1, 1);
+ ASSERT_EQ(0, memcmp(buf, "1", 1));
+
+ my_itos(buf, 10, 2);
+ ASSERT_EQ(0, memcmp(buf, "10", 2));
+
+ my_itos(buf, 63, 2);
+ ASSERT_EQ(0, memcmp(buf, "63", 2));
+
+ my_itos(buf, 101, 3);
+ ASSERT_EQ(0, memcmp(buf, "101", 2));
+}
+
+TEST(LinuxLibcSupportTest, strchr) {
+ ASSERT_EQ(NULL, my_strchr("abc", 'd'));
+ ASSERT_EQ(NULL, my_strchr("", 'd'));
+ ASSERT_EQ(NULL, my_strchr("efghi", 'd'));
+
+ ASSERT_TRUE(my_strchr("a", 'a'));
+ ASSERT_TRUE(my_strchr("abc", 'a'));
+ ASSERT_TRUE(my_strchr("bcda", 'a'));
+ ASSERT_TRUE(my_strchr("sdfasdf", 'a'));
+}
+
+TEST(LinuxLibcSupportTest, read_hex_ptr) {
+ uintptr_t result;
+ const char* last;
+
+ last = my_read_hex_ptr(&result, "");
+ ASSERT_EQ(result, 0);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0");
+ ASSERT_EQ(result, 0);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0123");
+ ASSERT_EQ(result, 0x123);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0123a");
+ ASSERT_EQ(result, 0x123a);
+ ASSERT_EQ(*last, 0);
+
+ last = my_read_hex_ptr(&result, "0123a-");
+ ASSERT_EQ(result, 0x123a);
+ ASSERT_EQ(*last, '-');
+}
diff --git a/breakpad/linux/linux_syscall_support.h b/breakpad/linux/linux_syscall_support.h
new file mode 100644
index 0000000..8f4de92
--- /dev/null
+++ b/breakpad/linux/linux_syscall_support.h
@@ -0,0 +1,2800 @@
+/* Copyright (c) 2005-2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * ---
+ * Author: Markus Gutschke
+ */
+
+/* This file includes Linux-specific support functions common to the
+ * coredumper and the thread lister; primarily, this is a collection
+ * of direct system calls, and a couple of symbols missing from
+ * standard header files.
+ * There are a few options that the including file can set to control
+ * the behavior of this file:
+ *
+ * SYS_CPLUSPLUS:
+ * The entire header file will normally be wrapped in 'extern "C" { }",
+ * making it suitable for compilation as both C and C++ source. If you
+ * do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit
+ * the wrapping. N.B. doing so will suppress inclusion of all prerequisite
+ * system header files, too. It is the caller's responsibility to provide
+ * the necessary definitions.
+ *
+ * SYS_ERRNO:
+ * All system calls will update "errno" unless overriden by setting the
+ * SYS_ERRNO macro prior to including this file. SYS_ERRNO should be
+ * an l-value.
+ *
+ * SYS_INLINE:
+ * New symbols will be defined "static inline", unless overridden by
+ * the SYS_INLINE macro.
+ *
+ * SYS_LINUX_SYSCALL_SUPPORT_H
+ * This macro is used to avoid multiple inclusions of this header file.
+ * If you need to include this file more than once, make sure to
+ * unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion.
+ *
+ * SYS_PREFIX:
+ * New system calls will have a prefix of "sys_" unless overridden by
+ * the SYS_PREFIX macro. Valid values for this macro are [0..9] which
+ * results in prefixes "sys[0..9]_". It is also possible to set this
+ * macro to -1, which avoids all prefixes.
+ *
+ * This file defines a few internal symbols that all start with "LSS_".
+ * Do not access these symbols from outside this file. They are not part
+ * of the supported API.
+ */
+#ifndef SYS_LINUX_SYSCALL_SUPPORT_H
+#define SYS_LINUX_SYSCALL_SUPPORT_H
+
+/* We currently only support x86-32, x86-64, ARM, MIPS, and PPC on Linux.
+ * Porting to other related platforms should not be difficult.
+ */
+#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
+ defined(__mips__) || defined(__PPC__)) && defined(__linux)
+
+#ifndef SYS_CPLUSPLUS
+#ifdef __cplusplus
+/* Some system header files in older versions of gcc neglect to properly
+ * handle being included from C++. As it appears to be harmless to have
+ * multiple nested 'extern "C"' blocks, just add another one here.
+ */
+extern "C" {
+#endif
+
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <string.h>
+#include <sys/ptrace.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <syscall.h>
+#include <unistd.h>
+#include <linux/unistd.h>
+#include <endian.h>
+
+#ifdef __mips__
+/* Include definitions of the ABI currently in use. */
+#include <sgidefs.h>
+#endif
+
+#endif
+
+/* As glibc often provides subtly incompatible data structures (and implicit
+ * wrapper functions that convert them), we provide our own kernel data
+ * structures for use by the system calls.
+ * These structures have been developed by using Linux 2.6.23 headers for
+ * reference. Note though, we do not care about exact API compatibility
+ * with the kernel, and in fact the kernel often does not have a single
+ * API that works across architectures. Instead, we try to mimic the glibc
+ * API where reasonable, and only guarantee ABI compatibility with the
+ * kernel headers.
+ * Most notably, here are a few changes that were made to the structures
+ * defined by kernel headers:
+ *
+ * - we only define structures, but not symbolic names for kernel data
+ * types. For the latter, we directly use the native C datatype
+ * (i.e. "unsigned" instead of "mode_t").
+ * - in a few cases, it is possible to define identical structures for
+ * both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by
+ * standardizing on the 64bit version of the data types. In particular,
+ * this means that we use "unsigned" where the 32bit headers say
+ * "unsigned long".
+ * - overall, we try to minimize the number of cases where we need to
+ * conditionally define different structures.
+ * - the "struct kernel_sigaction" class of structures have been
+ * modified to more closely mimic glibc's API by introducing an
+ * anonymous union for the function pointer.
+ * - a small number of field names had to have an underscore appended to
+ * them, because glibc defines a global macro by the same name.
+ */
+
+/* include/linux/dirent.h */
+struct kernel_dirent64 {
+ unsigned long long d_ino;
+ long long d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[256];
+};
+
+/* include/linux/dirent.h */
+struct kernel_dirent {
+ long d_ino;
+ long d_off;
+ unsigned short d_reclen;
+ char d_name[256];
+};
+
+/* include/linux/uio.h */
+struct kernel_iovec {
+ void *iov_base;
+ unsigned long iov_len;
+};
+
+/* include/linux/socket.h */
+struct kernel_msghdr {
+ void *msg_name;
+ int msg_namelen;
+ struct kernel_iovec*msg_iov;
+ unsigned long msg_iovlen;
+ void *msg_control;
+ unsigned long msg_controllen;
+ unsigned msg_flags;
+};
+
+/* include/asm-generic/poll.h */
+struct kernel_pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+/* include/linux/resource.h */
+struct kernel_rlimit {
+ unsigned long rlim_cur;
+ unsigned long rlim_max;
+};
+
+/* include/linux/time.h */
+struct kernel_timespec {
+ long tv_sec;
+ long tv_nsec;
+};
+
+/* include/linux/time.h */
+struct kernel_timeval {
+ long tv_sec;
+ long tv_usec;
+};
+
+/* include/linux/resource.h */
+struct kernel_rusage {
+ struct kernel_timeval ru_utime;
+ struct kernel_timeval ru_stime;
+ long ru_maxrss;
+ long ru_ixrss;
+ long ru_idrss;
+ long ru_isrss;
+ long ru_minflt;
+ long ru_majflt;
+ long ru_nswap;
+ long ru_inblock;
+ long ru_oublock;
+ long ru_msgsnd;
+ long ru_msgrcv;
+ long ru_nsignals;
+ long ru_nvcsw;
+ long ru_nivcsw;
+};
+
+struct siginfo;
+#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__PPC__)
+
+/* include/asm-{arm,i386,mips,ppc}/signal.h */
+struct kernel_old_sigaction {
+ union {
+ void (*sa_handler_)(int);
+ void (*sa_sigaction_)(int, struct siginfo *, void *);
+ };
+ unsigned long sa_mask;
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+} __attribute__((packed,aligned(4)));
+#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
+ #define kernel_old_sigaction kernel_sigaction
+#endif
+
+/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the
+ * exactly match the size of the signal set, even though the API was
+ * intended to be extensible. We define our own KERNEL_NSIG to deal with
+ * this.
+ * Please note that glibc provides signals [1.._NSIG-1], whereas the
+ * kernel (and this header) provides the range [1..KERNEL_NSIG]. The
+ * actual number of signals is obviously the same, but the constants
+ * differ by one.
+ */
+#ifdef __mips__
+#define KERNEL_NSIG 128
+#else
+#define KERNEL_NSIG 64
+#endif
+
+/* include/asm-{arm,i386,mips,x86_64}/signal.h */
+struct kernel_sigset_t {
+ unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/
+ (8*sizeof(unsigned long))];
+};
+
+/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h */
+struct kernel_sigaction {
+#ifdef __mips__
+ unsigned long sa_flags;
+ union {
+ void (*sa_handler_)(int);
+ void (*sa_sigaction_)(int, struct siginfo *, void *);
+ };
+ struct kernel_sigset_t sa_mask;
+#else
+ union {
+ void (*sa_handler_)(int);
+ void (*sa_sigaction_)(int, struct siginfo *, void *);
+ };
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+ struct kernel_sigset_t sa_mask;
+#endif
+};
+
+/* include/linux/socket.h */
+struct kernel_sockaddr {
+ unsigned short sa_family;
+ char sa_data[14];
+};
+
+/* include/asm-{arm,i386,mips,ppc}/stat.h */
+#ifdef __mips__
+#if _MIPS_SIM == _MIPS_SIM_ABI64
+struct kernel_stat {
+#else
+struct kernel_stat64 {
+#endif
+ unsigned st_dev;
+ unsigned __pad0[3];
+ unsigned long long st_ino;
+ unsigned st_mode;
+ unsigned st_nlink;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned st_rdev;
+ unsigned __pad1[3];
+ long long st_size;
+ unsigned st_atime_;
+ unsigned st_atime_nsec_;
+ unsigned st_mtime_;
+ unsigned st_mtime_nsec_;
+ unsigned st_ctime_;
+ unsigned st_ctime_nsec_;
+ unsigned st_blksize;
+ unsigned __pad2;
+ unsigned long long st_blocks;
+};
+#elif defined __PPC__
+struct kernel_stat64 {
+ unsigned long long st_dev;
+ unsigned long long st_ino;
+ unsigned st_mode;
+ unsigned st_nlink;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned long long st_rdev;
+ unsigned short int __pad2;
+ long long st_size;
+ long st_blksize;
+ long long st_blocks;
+ long st_atime_;
+ unsigned long st_atime_nsec_;
+ long st_mtime_;
+ unsigned long st_mtime_nsec_;
+ long st_ctime_;
+ unsigned long st_ctime_nsec_;
+ unsigned long __unused4;
+ unsigned long __unused5;
+};
+#else
+struct kernel_stat64 {
+ unsigned long long st_dev;
+ unsigned char __pad0[4];
+ unsigned __st_ino;
+ unsigned st_mode;
+ unsigned st_nlink;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned long long st_rdev;
+ unsigned char __pad3[4];
+ long long st_size;
+ unsigned st_blksize;
+ unsigned long long st_blocks;
+ unsigned st_atime_;
+ unsigned st_atime_nsec_;
+ unsigned st_mtime_;
+ unsigned st_mtime_nsec_;
+ unsigned st_ctime_;
+ unsigned st_ctime_nsec_;
+ unsigned long long st_ino;
+};
+#endif
+
+/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */
+#if defined(__i386__) || defined(__ARM_ARCH_3__)
+struct kernel_stat {
+ /* The kernel headers suggest that st_dev and st_rdev should be 32bit
+ * quantities encoding 12bit major and 20bit minor numbers in an interleaved
+ * format. In reality, we do not see useful data in the top bits. So,
+ * we'll leave the padding in here, until we find a better solution.
+ */
+ unsigned short st_dev;
+ short pad1;
+ unsigned st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ short pad2;
+ unsigned st_size;
+ unsigned st_blksize;
+ unsigned st_blocks;
+ unsigned st_atime_;
+ unsigned st_atime_nsec_;
+ unsigned st_mtime_;
+ unsigned st_mtime_nsec_;
+ unsigned st_ctime_;
+ unsigned st_ctime_nsec_;
+ unsigned __unused4;
+ unsigned __unused5;
+};
+#elif defined(__x86_64__)
+struct kernel_stat {
+ unsigned long st_dev;
+ unsigned long st_ino;
+ unsigned long st_nlink;
+ unsigned st_mode;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned __pad0;
+ unsigned long st_rdev;
+ long st_size;
+ long st_blksize;
+ long st_blocks;
+ unsigned long st_atime_;
+ unsigned long st_atime_nsec_;
+ unsigned long st_mtime_;
+ unsigned long st_mtime_nsec_;
+ unsigned long st_ctime_;
+ unsigned long st_ctime_nsec_;
+ long __unused[3];
+};
+#elif defined(__PPC__)
+struct kernel_stat {
+ unsigned st_dev;
+ unsigned long st_ino; // ino_t
+ unsigned long st_mode; // mode_t
+ unsigned short st_nlink; // nlink_t
+ unsigned st_uid; // uid_t
+ unsigned st_gid; // gid_t
+ unsigned st_rdev;
+ long st_size; // off_t
+ unsigned long st_blksize;
+ unsigned long st_blocks;
+ unsigned long st_atime_;
+ unsigned long st_atime_nsec_;
+ unsigned long st_mtime_;
+ unsigned long st_mtime_nsec_;
+ unsigned long st_ctime_;
+ unsigned long st_ctime_nsec_;
+ unsigned long __unused4;
+ unsigned long __unused5;
+};
+#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64)
+struct kernel_stat {
+ unsigned st_dev;
+ int st_pad1[3];
+ unsigned st_ino;
+ unsigned st_mode;
+ unsigned st_nlink;
+ unsigned st_uid;
+ unsigned st_gid;
+ unsigned st_rdev;
+ int st_pad2[2];
+ long st_size;
+ int st_pad3;
+ long st_atime_;
+ long st_atime_nsec_;
+ long st_mtime_;
+ long st_mtime_nsec_;
+ long st_ctime_;
+ long st_ctime_nsec_;
+ int st_blksize;
+ int st_blocks;
+ int st_pad4[14];
+};
+#endif
+
+/* include/asm-{arm,i386,mips,x86_64,ppc}/statfs.h */
+#ifdef __mips__
+#if _MIPS_SIM != _MIPS_SIM_ABI64
+struct kernel_statfs64 {
+ unsigned long f_type;
+ unsigned long f_bsize;
+ unsigned long f_frsize;
+ unsigned long __pad;
+ unsigned long long f_blocks;
+ unsigned long long f_bfree;
+ unsigned long long f_files;
+ unsigned long long f_ffree;
+ unsigned long long f_bavail;
+ struct { int val[2]; } f_fsid;
+ unsigned long f_namelen;
+ unsigned long f_spare[6];
+};
+#endif
+#elif !defined(__x86_64__)
+struct kernel_statfs64 {
+ unsigned long f_type;
+ unsigned long f_bsize;
+ unsigned long long f_blocks;
+ unsigned long long f_bfree;
+ unsigned long long f_bavail;
+ unsigned long long f_files;
+ unsigned long long f_ffree;
+ struct { int val[2]; } f_fsid;
+ unsigned long f_namelen;
+ unsigned long f_frsize;
+ unsigned long f_spare[5];
+};
+#endif
+
+/* include/asm-{arm,i386,mips,x86_64,ppc,generic}/statfs.h */
+#ifdef __mips__
+struct kernel_statfs {
+ long f_type;
+ long f_bsize;
+ long f_frsize;
+ long f_blocks;
+ long f_bfree;
+ long f_files;
+ long f_ffree;
+ long f_bavail;
+ struct { int val[2]; } f_fsid;
+ long f_namelen;
+ long f_spare[6];
+};
+#else
+struct kernel_statfs {
+ /* x86_64 actually defines all these fields as signed, whereas all other */
+ /* platforms define them as unsigned. Leaving them at unsigned should not */
+ /* cause any problems. */
+ unsigned long f_type;
+ unsigned long f_bsize;
+ unsigned long f_blocks;
+ unsigned long f_bfree;
+ unsigned long f_bavail;
+ unsigned long f_files;
+ unsigned long f_ffree;
+ struct { int val[2]; } f_fsid;
+ unsigned long f_namelen;
+ unsigned long f_frsize;
+ unsigned long f_spare[5];
+};
+#endif
+
+
+/* Definitions missing from the standard header files */
+#ifndef O_DIRECTORY
+#if defined(__ARM_ARCH_3__)
+#define O_DIRECTORY 0040000
+#else
+#define O_DIRECTORY 0200000
+#endif
+#endif
+#ifndef NT_PRXFPREG
+#define NT_PRXFPREG 0x46e62b7f
+#endif
+#ifndef PTRACE_GETFPXREGS
+#define PTRACE_GETFPXREGS ((enum __ptrace_request)18)
+#endif
+#ifndef PR_GET_DUMPABLE
+#define PR_GET_DUMPABLE 3
+#endif
+#ifndef PR_SET_DUMPABLE
+#define PR_SET_DUMPABLE 4
+#endif
+#ifndef AT_FDCWD
+#define AT_FDCWD (-100)
+#endif
+#ifndef AT_SYMLINK_NOFOLLOW
+#define AT_SYMLINK_NOFOLLOW 0x100
+#endif
+#ifndef AT_REMOVEDIR
+#define AT_REMOVEDIR 0x200
+#endif
+#ifndef MREMAP_FIXED
+#define MREMAP_FIXED 2
+#endif
+#ifndef SA_RESTORER
+#define SA_RESTORER 0x04000000
+#endif
+
+#if defined(__i386__)
+#ifndef __NR_setresuid
+#define __NR_setresuid 164
+#define __NR_setresgid 170
+#endif
+#ifndef __NR_rt_sigaction
+#define __NR_rt_sigaction 174
+#define __NR_rt_sigprocmask 175
+#define __NR_rt_sigpending 176
+#define __NR_rt_sigsuspend 179
+#endif
+#ifndef __NR_pread64
+#define __NR_pread64 180
+#endif
+#ifndef __NR_pwrite64
+#define __NR_pwrite64 181
+#endif
+#ifndef __NR_ugetrlimit
+#define __NR_ugetrlimit 191
+#endif
+#ifndef __NR_stat64
+#define __NR_stat64 195
+#endif
+#ifndef __NR_fstat64
+#define __NR_fstat64 197
+#endif
+#ifndef __NR_setresuid32
+#define __NR_setresuid32 208
+#define __NR_setresgid32 210
+#endif
+#ifndef __NR_setfsuid32
+#define __NR_setfsuid32 215
+#define __NR_setfsgid32 216
+#endif
+#ifndef __NR_getdents64
+#define __NR_getdents64 220
+#endif
+#ifndef __NR_gettid
+#define __NR_gettid 224
+#endif
+#ifndef __NR_readahead
+#define __NR_readahead 225
+#endif
+#ifndef __NR_setxattr
+#define __NR_setxattr 226
+#endif
+#ifndef __NR_lsetxattr
+#define __NR_lsetxattr 227
+#endif
+#ifndef __NR_getxattr
+#define __NR_getxattr 229
+#endif
+#ifndef __NR_lgetxattr
+#define __NR_lgetxattr 230
+#endif
+#ifndef __NR_futex
+#define __NR_futex 240
+#endif
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity 241
+#define __NR_sched_getaffinity 242
+#endif
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address 258
+#endif
+#ifndef __NR_statfs64
+#define __NR_statfs64 268
+#endif
+#ifndef __NR_fstatfs64
+#define __NR_fstatfs64 269
+#endif
+#ifndef __NR_fadvise64_64
+#define __NR_fadvise64_64 272
+#endif
+#ifndef __NR_openat
+#define __NR_openat 295
+#endif
+#ifndef __NR_fstatat64
+#define __NR_fstatat64 300
+#endif
+#ifndef __NR_unlinkat
+#define __NR_unlinkat 301
+#endif
+#ifndef __NR_move_pages
+#define __NR_move_pages 317
+#endif
+/* End of i386 definitions */
+#elif defined(__ARM_ARCH_3__)
+#ifndef __NR_setresuid
+#define __NR_setresuid (__NR_SYSCALL_BASE + 164)
+#define __NR_setresgid (__NR_SYSCALL_BASE + 170)
+#endif
+#ifndef __NR_rt_sigaction
+#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174)
+#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175)
+#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176)
+#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179)
+#endif
+#ifndef __NR_pread64
+#define __NR_pread64 (__NR_SYSCALL_BASE + 180)
+#endif
+#ifndef __NR_pwrite64
+#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181)
+#endif
+#ifndef __NR_ugetrlimit
+#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191)
+#endif
+#ifndef __NR_stat64
+#define __NR_stat64 (__NR_SYSCALL_BASE + 195)
+#endif
+#ifndef __NR_fstat64
+#define __NR_fstat64 (__NR_SYSCALL_BASE + 197)
+#endif
+#ifndef __NR_setresuid32
+#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208)
+#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210)
+#endif
+#ifndef __NR_setfsuid32
+#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215)
+#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216)
+#endif
+#ifndef __NR_getdents64
+#define __NR_getdents64 (__NR_SYSCALL_BASE + 217)
+#endif
+#ifndef __NR_gettid
+#define __NR_gettid (__NR_SYSCALL_BASE + 224)
+#endif
+#ifndef __NR_readahead
+#define __NR_readahead (__NR_SYSCALL_BASE + 225)
+#endif
+#ifndef __NR_setxattr
+#define __NR_setxattr (__NR_SYSCALL_BASE + 226)
+#endif
+#ifndef __NR_lsetxattr
+#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227)
+#endif
+#ifndef __NR_getxattr
+#define __NR_getxattr (__NR_SYSCALL_BASE + 229)
+#endif
+#ifndef __NR_lgetxattr
+#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230)
+#endif
+#ifndef __NR_futex
+#define __NR_futex (__NR_SYSCALL_BASE + 240)
+#endif
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241)
+#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242)
+#endif
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256)
+#endif
+#ifndef __NR_statfs64
+#define __NR_statfs64 (__NR_SYSCALL_BASE + 266)
+#endif
+#ifndef __NR_fstatfs64
+#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267)
+#endif
+#ifndef __NR_move_pages
+#define __NR_move_pages (__NR_SYSCALL_BASE + 344)
+#endif
+/* End of ARM 3 definitions */
+#elif defined(__x86_64__)
+#ifndef __NR_setresuid
+#define __NR_setresuid 117
+#define __NR_setresgid 119
+#endif
+#ifndef __NR_gettid
+#define __NR_gettid 186
+#endif
+#ifndef __NR_readahead
+#define __NR_readahead 187
+#endif
+#ifndef __NR_setxattr
+#define __NR_setxattr 188
+#endif
+#ifndef __NR_lsetxattr
+#define __NR_lsetxattr 189
+#endif
+#ifndef __NR_getxattr
+#define __NR_getxattr 191
+#endif
+#ifndef __NR_lgetxattr
+#define __NR_lgetxattr 192
+#endif
+#ifndef __NR_futex
+#define __NR_futex 202
+#endif
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity 203
+#define __NR_sched_getaffinity 204
+#endif
+#ifndef __NR_getdents64
+#define __NR_getdents64 217
+#endif
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address 218
+#endif
+#ifndef __NR_fadvise64
+#define __NR_fadvise64 221
+#endif
+#ifndef __NR_openat
+#define __NR_openat 257
+#endif
+#ifndef __NR_newfstatat
+#define __NR_newfstatat 262
+#endif
+#ifndef __NR_unlinkat
+#define __NR_unlinkat 263
+#endif
+#ifndef __NR_move_pages
+#define __NR_move_pages 279
+#endif
+/* End of x86-64 definitions */
+#elif defined(__mips__)
+#if _MIPS_SIM == _MIPS_SIM_ABI32
+#ifndef __NR_setresuid
+#define __NR_setresuid (__NR_Linux + 185)
+#define __NR_setresgid (__NR_Linux + 190)
+#endif
+#ifndef __NR_rt_sigaction
+#define __NR_rt_sigaction (__NR_Linux + 194)
+#define __NR_rt_sigprocmask (__NR_Linux + 195)
+#define __NR_rt_sigpending (__NR_Linux + 196)
+#define __NR_rt_sigsuspend (__NR_Linux + 199)
+#endif
+#ifndef __NR_pread64
+#define __NR_pread64 (__NR_Linux + 200)
+#endif
+#ifndef __NR_pwrite64
+#define __NR_pwrite64 (__NR_Linux + 201)
+#endif
+#ifndef __NR_stat64
+#define __NR_stat64 (__NR_Linux + 213)
+#endif
+#ifndef __NR_fstat64
+#define __NR_fstat64 (__NR_Linux + 215)
+#endif
+#ifndef __NR_getdents64
+#define __NR_getdents64 (__NR_Linux + 219)
+#endif
+#ifndef __NR_gettid
+#define __NR_gettid (__NR_Linux + 222)
+#endif
+#ifndef __NR_readahead
+#define __NR_readahead (__NR_Linux + 223)
+#endif
+#ifndef __NR_setxattr
+#define __NR_setxattr (__NR_Linux + 224)
+#endif
+#ifndef __NR_lsetxattr
+#define __NR_lsetxattr (__NR_Linux + 225)
+#endif
+#ifndef __NR_getxattr
+#define __NR_getxattr (__NR_Linux + 227)
+#endif
+#ifndef __NR_lgetxattr
+#define __NR_lgetxattr (__NR_Linux + 228)
+#endif
+#ifndef __NR_futex
+#define __NR_futex (__NR_Linux + 238)
+#endif
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity (__NR_Linux + 239)
+#define __NR_sched_getaffinity (__NR_Linux + 240)
+#endif
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address (__NR_Linux + 252)
+#endif
+#ifndef __NR_statfs64
+#define __NR_statfs64 (__NR_Linux + 255)
+#endif
+#ifndef __NR_fstatfs64
+#define __NR_fstatfs64 (__NR_Linux + 256)
+#endif
+#ifndef __NR_openat
+#define __NR_openat (__NR_Linux + 288)
+#endif
+#ifndef __NR_fstatat
+#define __NR_fstatat (__NR_Linux + 293)
+#endif
+#ifndef __NR_unlinkat
+#define __NR_unlinkat (__NR_Linux + 294)
+#endif
+#ifndef __NR_move_pages
+#define __NR_move_pages (__NR_Linux + 308)
+#endif
+/* End of MIPS (old 32bit API) definitions */
+#elif _MIPS_SIM == _MIPS_SIM_ABI64
+#ifndef __NR_setresuid
+#define __NR_setresuid (__NR_Linux + 115)
+#define __NR_setresgid (__NR_Linux + 117)
+#endif
+#ifndef __NR_gettid
+#define __NR_gettid (__NR_Linux + 178)
+#endif
+#ifndef __NR_readahead
+#define __NR_readahead (__NR_Linux + 179)
+#endif
+#ifndef __NR_setxattr
+#define __NR_setxattr (__NR_Linux + 180)
+#endif
+#ifndef __NR_lsetxattr
+#define __NR_lsetxattr (__NR_Linux + 181)
+#endif
+#ifndef __NR_getxattr
+#define __NR_getxattr (__NR_Linux + 183)
+#endif
+#ifndef __NR_lgetxattr
+#define __NR_lgetxattr (__NR_Linux + 184)
+#endif
+#ifndef __NR_futex
+#define __NR_futex (__NR_Linux + 194)
+#endif
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity (__NR_Linux + 195)
+#define __NR_sched_getaffinity (__NR_Linux + 196)
+#endif
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address (__NR_Linux + 212)
+#endif
+#ifndef __NR_openat
+#define __NR_openat (__NR_Linux + 247)
+#endif
+#ifndef __NR_fstatat
+#define __NR_fstatat (__NR_Linux + 252)
+#endif
+#ifndef __NR_unlinkat
+#define __NR_unlinkat (__NR_Linux + 253)
+#endif
+#ifndef __NR_move_pages
+#define __NR_move_pages (__NR_Linux + 267)
+#endif
+/* End of MIPS (64bit API) definitions */
+#else
+#ifndef __NR_setresuid
+#define __NR_setresuid (__NR_Linux + 115)
+#define __NR_setresgid (__NR_Linux + 117)
+#endif
+#ifndef __NR_gettid
+#define __NR_gettid (__NR_Linux + 178)
+#endif
+#ifndef __NR_readahead
+#define __NR_readahead (__NR_Linux + 179)
+#endif
+#ifndef __NR_setxattr
+#define __NR_setxattr (__NR_Linux + 180)
+#endif
+#ifndef __NR_lsetxattr
+#define __NR_lsetxattr (__NR_Linux + 181)
+#endif
+#ifndef __NR_getxattr
+#define __NR_getxattr (__NR_Linux + 183)
+#endif
+#ifndef __NR_lgetxattr
+#define __NR_lgetxattr (__NR_Linux + 184)
+#endif
+#ifndef __NR_futex
+#define __NR_futex (__NR_Linux + 194)
+#endif
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity (__NR_Linux + 195)
+#define __NR_sched_getaffinity (__NR_Linux + 196)
+#endif
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address (__NR_Linux + 213)
+#endif
+#ifndef __NR_statfs64
+#define __NR_statfs64 (__NR_Linux + 217)
+#endif
+#ifndef __NR_fstatfs64
+#define __NR_fstatfs64 (__NR_Linux + 218)
+#endif
+#ifndef __NR_openat
+#define __NR_openat (__NR_Linux + 251)
+#endif
+#ifndef __NR_fstatat
+#define __NR_fstatat (__NR_Linux + 256)
+#endif
+#ifndef __NR_unlinkat
+#define __NR_unlinkat (__NR_Linux + 257)
+#endif
+#ifndef __NR_move_pages
+#define __NR_move_pages (__NR_Linux + 271)
+#endif
+/* End of MIPS (new 32bit API) definitions */
+#endif
+/* End of MIPS definitions */
+#elif defined(__PPC__)
+#ifndef __NR_setfsuid
+#define __NR_setfsuid 138
+#define __NR_setfsgid 139
+#endif
+#ifndef __NR_setresuid
+#define __NR_setresuid 164
+#define __NR_setresgid 169
+#endif
+#ifndef __NR_rt_sigaction
+#define __NR_rt_sigaction 173
+#define __NR_rt_sigprocmask 174
+#define __NR_rt_sigpending 175
+#define __NR_rt_sigsuspend 178
+#endif
+#ifndef __NR_pread64
+#define __NR_pread64 179
+#endif
+#ifndef __NR_pwrite64
+#define __NR_pwrite64 180
+#endif
+#ifndef __NR_ugetrlimit
+#define __NR_ugetrlimit 190
+#endif
+#ifndef __NR_readahead
+#define __NR_readahead 191
+#endif
+#ifndef __NR_stat64
+#define __NR_stat64 195
+#endif
+#ifndef __NR_fstat64
+#define __NR_fstat64 197
+#endif
+#ifndef __NR_getdents64
+#define __NR_getdents64 202
+#endif
+#ifndef __NR_gettid
+#define __NR_gettid 207
+#endif
+#ifndef __NR_setxattr
+#define __NR_setxattr 209
+#endif
+#ifndef __NR_lsetxattr
+#define __NR_lsetxattr 210
+#endif
+#ifndef __NR_getxattr
+#define __NR_getxattr 212
+#endif
+#ifndef __NR_lgetxattr
+#define __NR_lgetxattr 213
+#endif
+#ifndef __NR_futex
+#define __NR_futex 221
+#endif
+#ifndef __NR_sched_setaffinity
+#define __NR_sched_setaffinity 222
+#define __NR_sched_getaffinity 223
+#endif
+#ifndef __NR_set_tid_address
+#define __NR_set_tid_address 232
+#endif
+#ifndef __NR_statfs64
+#define __NR_statfs64 252
+#endif
+#ifndef __NR_fstatfs64
+#define __NR_fstatfs64 253
+#endif
+#ifndef __NR_fadvise64_64
+#define __NR_fadvise64_64 254
+#endif
+#ifndef __NR_openat
+#define __NR_openat 286
+#endif
+#ifndef __NR_fstatat64
+#define __NR_fstatat64 291
+#endif
+#ifndef __NR_unlinkat
+#define __NR_unlinkat 292
+#endif
+#ifndef __NR_move_pages
+#define __NR_move_pages 301
+#endif
+/* End of powerpc defininitions */
+#endif
+
+
+/* After forking, we must make sure to only call system calls. */
+#if __BOUNDED_POINTERS__
+ #error "Need to port invocations of syscalls for bounded ptrs"
+#else
+ /* The core dumper and the thread lister get executed after threads
+ * have been suspended. As a consequence, we cannot call any functions
+ * that acquire locks. Unfortunately, libc wraps most system calls
+ * (e.g. in order to implement pthread_atfork, and to make calls
+ * cancellable), which means we cannot call these functions. Instead,
+ * we have to call syscall() directly.
+ */
+ #undef LSS_ERRNO
+ #ifdef SYS_ERRNO
+ /* Allow the including file to override the location of errno. This can
+ * be useful when using clone() with the CLONE_VM option.
+ */
+ #define LSS_ERRNO SYS_ERRNO
+ #else
+ #define LSS_ERRNO errno
+ #endif
+
+ #undef LSS_INLINE
+ #ifdef SYS_INLINE
+ #define LSS_INLINE SYS_INLINE
+ #else
+ #define LSS_INLINE static inline
+ #endif
+
+ /* Allow the including file to override the prefix used for all new
+ * system calls. By default, it will be set to "sys_".
+ */
+ #undef LSS_NAME
+ #ifndef SYS_PREFIX
+ #define LSS_NAME(name) sys_##name
+ #elif SYS_PREFIX < 0
+ #define LSS_NAME(name) name
+ #elif SYS_PREFIX == 0
+ #define LSS_NAME(name) sys0_##name
+ #elif SYS_PREFIX == 1
+ #define LSS_NAME(name) sys1_##name
+ #elif SYS_PREFIX == 2
+ #define LSS_NAME(name) sys2_##name
+ #elif SYS_PREFIX == 3
+ #define LSS_NAME(name) sys3_##name
+ #elif SYS_PREFIX == 4
+ #define LSS_NAME(name) sys4_##name
+ #elif SYS_PREFIX == 5
+ #define LSS_NAME(name) sys5_##name
+ #elif SYS_PREFIX == 6
+ #define LSS_NAME(name) sys6_##name
+ #elif SYS_PREFIX == 7
+ #define LSS_NAME(name) sys7_##name
+ #elif SYS_PREFIX == 8
+ #define LSS_NAME(name) sys8_##name
+ #elif SYS_PREFIX == 9
+ #define LSS_NAME(name) sys9_##name
+ #endif
+
+ #undef LSS_RETURN
+ #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__))
+ /* Failing system calls return a negative result in the range of
+ * -1..-4095. These are "errno" values with the sign inverted.
+ */
+ #define LSS_RETURN(type, res) \
+ do { \
+ if ((unsigned long)(res) >= (unsigned long)(-4095)) { \
+ LSS_ERRNO = -(res); \
+ res = -1; \
+ } \
+ return (type) (res); \
+ } while (0)
+ #elif defined(__mips__)
+ /* On MIPS, failing system calls return -1, and set errno in a
+ * separate CPU register.
+ */
+ #define LSS_RETURN(type, res, err) \
+ do { \
+ if (err) { \
+ LSS_ERRNO = (res); \
+ res = -1; \
+ } \
+ return (type) (res); \
+ } while (0)
+ #elif defined(__PPC__)
+ /* On PPC, failing system calls return -1, and set errno in a
+ * separate CPU register. See linux/unistd.h.
+ */
+ #define LSS_RETURN(type, res, err) \
+ do { \
+ if (err & 0x10000000 ) { \
+ LSS_ERRNO = (res); \
+ res = -1; \
+ } \
+ return (type) (res); \
+ } while (0)
+ #endif
+ #if defined(__i386__)
+ /* In PIC mode (e.g. when building shared libraries), gcc for i386
+ * reserves ebx. Unfortunately, most distribution ship with implementations
+ * of _syscallX() which clobber ebx.
+ * Also, most definitions of _syscallX() neglect to mark "memory" as being
+ * clobbered. This causes problems with compilers, that do a better job
+ * at optimizing across __asm__ calls.
+ * So, we just have to redefine all of the _syscallX() macros.
+ */
+ #undef LSS_BODY
+ #define LSS_BODY(type,args...) \
+ long __res; \
+ __asm__ __volatile__("push %%ebx\n" \
+ "movl %2,%%ebx\n" \
+ "int $0x80\n" \
+ "pop %%ebx" \
+ args \
+ : "memory"); \
+ LSS_RETURN(type,__res)
+ #undef _syscall0
+ #define _syscall0(type,name) \
+ type LSS_NAME(name)(void) { \
+ long __res; \
+ __asm__ volatile("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name) \
+ : "memory"); \
+ LSS_RETURN(type,__res); \
+ }
+ #undef _syscall1
+ #define _syscall1(type,name,type1,arg1) \
+ type LSS_NAME(name)(type1 arg1) { \
+ LSS_BODY(type, \
+ : "=a" (__res) \
+ : "0" (__NR_##name), "ri" ((long)(arg1))); \
+ }
+ #undef _syscall2
+ #define _syscall2(type,name,type1,arg1,type2,arg2) \
+ type LSS_NAME(name)(type1 arg1,type2 arg2) { \
+ LSS_BODY(type, \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2))); \
+ }
+ #undef _syscall3
+ #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+ type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) { \
+ LSS_BODY(type, \
+ : "=a" (__res) \
+ : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \
+ "d" ((long)(arg3))); \
+ }
+ #undef _syscall4
+ #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ LSS_BODY(type, \
+ : "=a" (__res) \
+ : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \
+ "d" ((long)(arg3)),"S" ((long)(arg4))); \
+ }
+ #undef _syscall5
+ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) { \
+ long __res; \
+ __asm__ __volatile__("push %%ebx\n" \
+ "movl %2,%%ebx\n" \
+ "movl %1,%%eax\n" \
+ "int $0x80\n" \
+ "pop %%ebx" \
+ : "=a" (__res) \
+ : "i" (__NR_##name), "ri" ((long)(arg1)), \
+ "c" ((long)(arg2)), "d" ((long)(arg3)), \
+ "S" ((long)(arg4)), "D" ((long)(arg5)) \
+ : "memory"); \
+ LSS_RETURN(type,__res); \
+ }
+ #undef _syscall6
+ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) { \
+ long __res; \
+ struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 }; \
+ __asm__ __volatile__("push %%ebp\n" \
+ "push %%ebx\n" \
+ "movl 4(%2),%%ebp\n" \
+ "movl 0(%2), %%ebx\n" \
+ "movl %1,%%eax\n" \
+ "int $0x80\n" \
+ "pop %%ebx\n" \
+ "pop %%ebp" \
+ : "=a" (__res) \
+ : "i" (__NR_##name), "0" ((long)(&__s)), \
+ "c" ((long)(arg2)), "d" ((long)(arg3)), \
+ "S" ((long)(arg4)), "D" ((long)(arg5)) \
+ : "memory"); \
+ LSS_RETURN(type,__res); \
+ }
+ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, int *parent_tidptr,
+ void *newtls, int *child_tidptr) {
+ long __res;
+ __asm__ __volatile__(/* if (fn == NULL)
+ * return -EINVAL;
+ */
+ "movl %3,%%ecx\n"
+ "jecxz 1f\n"
+
+ /* if (child_stack == NULL)
+ * return -EINVAL;
+ */
+ "movl %4,%%ecx\n"
+ "jecxz 1f\n"
+
+ /* Set up alignment of the child stack:
+ * child_stack = (child_stack & ~0xF) - 20;
+ */
+ "andl $-16,%%ecx\n"
+ "subl $20,%%ecx\n"
+
+ /* Push "arg" and "fn" onto the stack that will be
+ * used by the child.
+ */
+ "movl %6,%%eax\n"
+ "movl %%eax,4(%%ecx)\n"
+ "movl %3,%%eax\n"
+ "movl %%eax,(%%ecx)\n"
+
+ /* %eax = syscall(%eax = __NR_clone,
+ * %ebx = flags,
+ * %ecx = child_stack,
+ * %edx = parent_tidptr,
+ * %esi = newtls,
+ * %edi = child_tidptr)
+ * Also, make sure that %ebx gets preserved as it is
+ * used in PIC mode.
+ */
+ "movl %8,%%esi\n"
+ "movl %7,%%edx\n"
+ "movl %5,%%eax\n"
+ "movl %9,%%edi\n"
+ "pushl %%ebx\n"
+ "movl %%eax,%%ebx\n"
+ "movl %2,%%eax\n"
+ "int $0x80\n"
+
+ /* In the parent: restore %ebx
+ * In the child: move "fn" into %ebx
+ */
+ "popl %%ebx\n"
+
+ /* if (%eax != 0)
+ * return %eax;
+ */
+ "test %%eax,%%eax\n"
+ "jnz 1f\n"
+
+ /* In the child, now. Terminate frame pointer chain.
+ */
+ "movl $0,%%ebp\n"
+
+ /* Call "fn". "arg" is already on the stack.
+ */
+ "call *%%ebx\n"
+
+ /* Call _exit(%ebx). Unfortunately older versions
+ * of gcc restrict the number of arguments that can
+ * be passed to asm(). So, we need to hard-code the
+ * system call number.
+ */
+ "movl %%eax,%%ebx\n"
+ "movl $1,%%eax\n"
+ "int $0x80\n"
+
+ /* Return to parent.
+ */
+ "1:\n"
+ : "=a" (__res)
+ : "0"(-EINVAL), "i"(__NR_clone),
+ "m"(fn), "m"(child_stack), "m"(flags), "m"(arg),
+ "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr)
+ : "memory", "ecx", "edx", "esi", "edi");
+ LSS_RETURN(int, __res);
+ }
+
+ #define __NR__fadvise64_64 __NR_fadvise64_64
+ LSS_INLINE _syscall6(int, _fadvise64_64, int, fd,
+ unsigned, offset_lo, unsigned, offset_hi,
+ unsigned, len_lo, unsigned, len_hi,
+ int, advice)
+
+ LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset,
+ loff_t len, int advice) {
+ return LSS_NAME(_fadvise64_64)(fd,
+ (unsigned)offset, (unsigned)(offset >>32),
+ (unsigned)len, (unsigned)(len >> 32),
+ advice);
+ }
+
+ LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) {
+ /* On i386, the kernel does not know how to return from a signal
+ * handler. Instead, it relies on user space to provide a
+ * restorer function that calls the {rt_,}sigreturn() system call.
+ * Unfortunately, we cannot just reference the glibc version of this
+ * function, as glibc goes out of its way to make it inaccessible.
+ */
+ void (*res)(void);
+ __asm__ __volatile__("call 2f\n"
+ "0:.align 16\n"
+ "1:movl %1,%%eax\n"
+ "int $0x80\n"
+ "2:popl %0\n"
+ "addl $(1b-0b),%0\n"
+ : "=a" (res)
+ : "i" (__NR_rt_sigreturn));
+ return res;
+ }
+ LSS_INLINE void (*LSS_NAME(restore)(void))(void) {
+ /* On i386, the kernel does not know how to return from a signal
+ * handler. Instead, it relies on user space to provide a
+ * restorer function that calls the {rt_,}sigreturn() system call.
+ * Unfortunately, we cannot just reference the glibc version of this
+ * function, as glibc goes out of its way to make it inaccessible.
+ */
+ void (*res)(void);
+ __asm__ __volatile__("call 2f\n"
+ "0:.align 16\n"
+ "1:pop %%eax\n"
+ "movl %1,%%eax\n"
+ "int $0x80\n"
+ "2:popl %0\n"
+ "addl $(1b-0b),%0\n"
+ : "=a" (res)
+ : "i" (__NR_sigreturn));
+ return res;
+ }
+ #elif defined(__x86_64__)
+ /* There are no known problems with any of the _syscallX() macros
+ * currently shipping for x86_64, but we still need to be able to define
+ * our own version so that we can override the location of the errno
+ * location (e.g. when using the clone() system call with the CLONE_VM
+ * option).
+ */
+ #undef LSS_BODY
+ #define LSS_BODY(type,name, ...) \
+ long __res; \
+ __asm__ __volatile__("syscall" : "=a" (__res) : "0" (__NR_##name), \
+ ##__VA_ARGS__ : "r11", "rcx", "memory"); \
+ LSS_RETURN(type, __res)
+ #undef _syscall0
+ #define _syscall0(type,name) \
+ type LSS_NAME(name)() { \
+ LSS_BODY(type, name); \
+ }
+ #undef _syscall1
+ #define _syscall1(type,name,type1,arg1) \
+ type LSS_NAME(name)(type1 arg1) { \
+ LSS_BODY(type, name, "D" ((long)(arg1))); \
+ }
+ #undef _syscall2
+ #define _syscall2(type,name,type1,arg1,type2,arg2) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2) { \
+ LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2))); \
+ }
+ #undef _syscall3
+ #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
+ LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2)), \
+ "d" ((long)(arg3))); \
+ }
+ #undef _syscall4
+ #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ long __res; \
+ __asm__ __volatile__("movq %5,%%r10; syscall" : \
+ "=a" (__res) : "0" (__NR_##name), \
+ "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \
+ "g" ((long)(arg4)) : "r10", "r11", "rcx", "memory"); \
+ LSS_RETURN(type, __res); \
+ }
+ #undef _syscall5
+ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) { \
+ long __res; \
+ __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; syscall" : \
+ "=a" (__res) : "0" (__NR_##name), \
+ "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \
+ "g" ((long)(arg4)), "g" ((long)(arg5)) : \
+ "r8", "r10", "r11", "rcx", "memory"); \
+ LSS_RETURN(type, __res); \
+ }
+ #undef _syscall6
+ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) { \
+ long __res; \
+ __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; movq %7,%%r9;" \
+ "syscall" : \
+ "=a" (__res) : "0" (__NR_##name), \
+ "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \
+ "g" ((long)(arg4)), "g" ((long)(arg5)), "g" ((long)(arg6)) : \
+ "r8", "r9", "r10", "r11", "rcx", "memory"); \
+ LSS_RETURN(type, __res); \
+ }
+ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, int *parent_tidptr,
+ void *newtls, int *child_tidptr) {
+ long __res;
+ {
+ register void *__tls __asm__("r8") = newtls;
+ register int *__ctid __asm__("r10") = child_tidptr;
+ __asm__ __volatile__(/* if (fn == NULL)
+ * return -EINVAL;
+ */
+ "testq %4,%4\n"
+ "jz 1f\n"
+
+ /* if (child_stack == NULL)
+ * return -EINVAL;
+ */
+ "testq %5,%5\n"
+ "jz 1f\n"
+
+ /* childstack -= 2*sizeof(void *);
+ */
+ "subq $16,%5\n"
+
+ /* Push "arg" and "fn" onto the stack that will be
+ * used by the child.
+ */
+ "movq %7,8(%5)\n"
+ "movq %4,0(%5)\n"
+
+ /* %rax = syscall(%rax = __NR_clone,
+ * %rdi = flags,
+ * %rsi = child_stack,
+ * %rdx = parent_tidptr,
+ * %r8 = new_tls,
+ * %r10 = child_tidptr)
+ */
+ "movq %2,%%rax\n"
+ "syscall\n"
+
+ /* if (%rax != 0)
+ * return;
+ */
+ "testq %%rax,%%rax\n"
+ "jnz 1f\n"
+
+ /* In the child. Terminate frame pointer chain.
+ */
+ "xorq %%rbp,%%rbp\n"
+
+ /* Call "fn(arg)".
+ */
+ "popq %%rax\n"
+ "popq %%rdi\n"
+ "call *%%rax\n"
+
+ /* Call _exit(%ebx).
+ */
+ "movq %%rax,%%rdi\n"
+ "movq %3,%%rax\n"
+ "syscall\n"
+
+ /* Return to parent.
+ */
+ "1:\n"
+ : "=a" (__res)
+ : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit),
+ "r"(fn), "S"(child_stack), "D"(flags), "r"(arg),
+ "d"(parent_tidptr), "r"(__tls), "r"(__ctid)
+ : "memory", "r11", "rcx");
+ }
+ LSS_RETURN(int, __res);
+ }
+ LSS_INLINE _syscall4(int, fadvise64, int, fd, loff_t, offset, loff_t, len,
+ int, advice)
+
+ LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) {
+ /* On x86-64, the kernel does not know how to return from
+ * a signal handler. Instead, it relies on user space to provide a
+ * restorer function that calls the rt_sigreturn() system call.
+ * Unfortunately, we cannot just reference the glibc version of this
+ * function, as glibc goes out of its way to make it inaccessible.
+ */
+ void (*res)(void);
+ __asm__ __volatile__("call 2f\n"
+ "0:.align 16\n"
+ "1:movq %1,%%rax\n"
+ "syscall\n"
+ "2:popq %0\n"
+ "addq $(1b-0b),%0\n"
+ : "=a" (res)
+ : "i" (__NR_rt_sigreturn));
+ return res;
+ }
+ #elif defined(__ARM_ARCH_3__)
+ /* Most definitions of _syscallX() neglect to mark "memory" as being
+ * clobbered. This causes problems with compilers, that do a better job
+ * at optimizing across __asm__ calls.
+ * So, we just have to redefine all fo the _syscallX() macros.
+ */
+ #undef LSS_REG
+ #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a
+ #undef LSS_BODY
+ #define LSS_BODY(type,name,args...) \
+ register long __res_r0 __asm__("r0"); \
+ long __res; \
+ __asm__ __volatile__ (__syscall(name) \
+ : "=r"(__res_r0) : args : "lr", "memory"); \
+ __res = __res_r0; \
+ LSS_RETURN(type, __res)
+ #undef _syscall0
+ #define _syscall0(type, name) \
+ type LSS_NAME(name)() { \
+ LSS_BODY(type, name); \
+ }
+ #undef _syscall1
+ #define _syscall1(type, name, type1, arg1) \
+ type LSS_NAME(name)(type1 arg1) { \
+ LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \
+ }
+ #undef _syscall2
+ #define _syscall2(type, name, type1, arg1, type2, arg2) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \
+ }
+ #undef _syscall3
+ #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \
+ }
+ #undef _syscall4
+ #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_REG(3, arg4); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \
+ }
+ #undef _syscall5
+ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_REG(3, arg4); LSS_REG(4, arg5); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
+ "r"(__r4)); \
+ }
+ #undef _syscall6
+ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) { \
+ LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \
+ LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \
+ LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \
+ "r"(__r4), "r"(__r5)); \
+ }
+ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, int *parent_tidptr,
+ void *newtls, int *child_tidptr) {
+ long __res;
+ {
+ register int __flags __asm__("r0") = flags;
+ register void *__stack __asm__("r1") = child_stack;
+ register void *__ptid __asm__("r2") = parent_tidptr;
+ register void *__tls __asm__("r3") = newtls;
+ register int *__ctid __asm__("r4") = child_tidptr;
+ __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL)
+ * return -EINVAL;
+ */
+ "cmp %2,#0\n"
+ "cmpne %3,#0\n"
+ "moveq %0,%1\n"
+ "beq 1f\n"
+
+ /* Push "arg" and "fn" onto the stack that will be
+ * used by the child.
+ */
+ "str %5,[%3,#-4]!\n"
+ "str %2,[%3,#-4]!\n"
+
+ /* %r0 = syscall(%r0 = flags,
+ * %r1 = child_stack,
+ * %r2 = parent_tidptr,
+ * %r3 = newtls,
+ * %r4 = child_tidptr)
+ */
+ __syscall(clone)"\n"
+
+ /* if (%r0 != 0)
+ * return %r0;
+ */
+ "movs %0,r0\n"
+ "bne 1f\n"
+
+ /* In the child, now. Call "fn(arg)".
+ */
+ "ldr r0,[sp, #4]\n"
+ "mov lr,pc\n"
+ "ldr pc,[sp]\n"
+
+ /* Call _exit(%r0).
+ */
+ __syscall(exit)"\n"
+ "1:\n"
+ : "=r" (__res)
+ : "i"(-EINVAL),
+ "r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
+ "r"(__ptid), "r"(__tls), "r"(__ctid)
+ : "lr", "memory");
+ }
+ LSS_RETURN(int, __res);
+ }
+ #elif defined(__mips__)
+ #undef LSS_REG
+ #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \
+ (unsigned long)(a)
+ #undef LSS_BODY
+ #define LSS_BODY(type,name,r7,...) \
+ register unsigned long __v0 __asm__("$2") = __NR_##name; \
+ __asm__ __volatile__ ("syscall\n" \
+ : "=&r"(__v0), r7 (__r7) \
+ : "0"(__v0), ##__VA_ARGS__ \
+ : "$8", "$9", "$10", "$11", "$12", \
+ "$13", "$14", "$15", "$24", "memory"); \
+ LSS_RETURN(type, __v0, __r7)
+ #undef _syscall0
+ #define _syscall0(type, name) \
+ type LSS_NAME(name)() { \
+ register unsigned long __r7 __asm__("$7"); \
+ LSS_BODY(type, name, "=r"); \
+ }
+ #undef _syscall1
+ #define _syscall1(type, name, type1, arg1) \
+ type LSS_NAME(name)(type1 arg1) { \
+ register unsigned long __r7 __asm__("$7"); \
+ LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4)); \
+ }
+ #undef _syscall2
+ #define _syscall2(type, name, type1, arg1, type2, arg2) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2) { \
+ register unsigned long __r7 __asm__("$7"); \
+ LSS_REG(4, arg1); LSS_REG(5, arg2); \
+ LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5)); \
+ }
+ #undef _syscall3
+ #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
+ register unsigned long __r7 __asm__("$7"); \
+ LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \
+ LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6)); \
+ }
+ #undef _syscall4
+ #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \
+ LSS_REG(7, arg4); \
+ LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6)); \
+ }
+ #undef _syscall5
+ #if _MIPS_SIM == _MIPS_SIM_ABI32
+ /* The old 32bit MIPS system call API passes the fifth and sixth argument
+ * on the stack, whereas the new APIs use registers "r8" and "r9".
+ */
+ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) { \
+ LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \
+ LSS_REG(7, arg4); \
+ register unsigned long __v0 __asm__("$2"); \
+ __asm__ __volatile__ (".set noreorder\n" \
+ "lw $2, %6\n" \
+ "subu $29, 32\n" \
+ "sw $2, 16($29)\n" \
+ "li $2, %2\n" \
+ "syscall\n" \
+ "addiu $29, 32\n" \
+ ".set reorder\n" \
+ : "=&r"(__v0), "+r" (__r7) \
+ : "i" (__NR_##name), "r"(__r4), "r"(__r5), \
+ "r"(__r6), "m" ((unsigned long)arg5) \
+ : "$8", "$9", "$10", "$11", "$12", \
+ "$13", "$14", "$15", "$24", "memory"); \
+ LSS_RETURN(type, __v0, __r7); \
+ }
+ #else
+ #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) { \
+ LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \
+ LSS_REG(7, arg4); LSS_REG(8, arg5); \
+ LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \
+ "r"(__r8)); \
+ }
+ #endif
+ #undef _syscall6
+ #if _MIPS_SIM == _MIPS_SIM_ABI32
+ /* The old 32bit MIPS system call API passes the fifth and sixth argument
+ * on the stack, whereas the new APIs use registers "r8" and "r9".
+ */
+ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) { \
+ LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \
+ LSS_REG(7, arg4); \
+ register unsigned long __v0 __asm__("$2"); \
+ __asm__ __volatile__ (".set noreorder\n" \
+ "lw $2, %6\n" \
+ "lw $8, %7\n" \
+ "subu $29, 32\n" \
+ "sw $2, 16($29)\n" \
+ "sw $8, 20($29)\n" \
+ "li $2, %2\n" \
+ "syscall\n" \
+ "addiu $29, 32\n" \
+ ".set reorder\n" \
+ : "=&r"(__v0), "+r" (__r7) \
+ : "i" (__NR_##name), "r"(__r4), "r"(__r5), \
+ "r"(__r6), "r" ((unsigned long)arg5), \
+ "r" ((unsigned long)arg6) \
+ : "$8", "$9", "$10", "$11", "$12", \
+ "$13", "$14", "$15", "$24", "memory"); \
+ LSS_RETURN(type, __v0, __r7); \
+ }
+ #else
+ #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5,type6,arg6) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5,type6 arg6) { \
+ LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \
+ LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6); \
+ LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \
+ "r"(__r8), "r"(__r9)); \
+ }
+ #endif
+ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, int *parent_tidptr,
+ void *newtls, int *child_tidptr) {
+ register unsigned long __v0 __asm__("$2");
+ register unsigned long __r7 __asm__("$7") = (unsigned long)newtls;
+ {
+ register int __flags __asm__("$4") = flags;
+ register void *__stack __asm__("$5") = child_stack;
+ register void *__ptid __asm__("$6") = parent_tidptr;
+ register int *__ctid __asm__("$8") = child_tidptr;
+ __asm__ __volatile__(
+ #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
+ "subu $29,24\n"
+ #elif _MIPS_SIM == _MIPS_SIM_NABI32
+ "sub $29,16\n"
+ #else
+ "dsubu $29,16\n"
+ #endif
+
+ /* if (fn == NULL || child_stack == NULL)
+ * return -EINVAL;
+ */
+ "li %0,%2\n"
+ "beqz %5,1f\n"
+ "beqz %6,1f\n"
+
+ /* Push "arg" and "fn" onto the stack that will be
+ * used by the child.
+ */
+ #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
+ "subu %6,32\n"
+ "sw %5,0(%6)\n"
+ "sw %8,4(%6)\n"
+ #elif _MIPS_SIM == _MIPS_SIM_NABI32
+ "sub %6,32\n"
+ "sw %5,0(%6)\n"
+ "sw %8,8(%6)\n"
+ #else
+ "dsubu %6,32\n"
+ "sd %5,0(%6)\n"
+ "sd %8,8(%6)\n"
+ #endif
+
+ /* $7 = syscall($4 = flags,
+ * $5 = child_stack,
+ * $6 = parent_tidptr,
+ * $7 = newtls,
+ * $8 = child_tidptr)
+ */
+ "li $2,%3\n"
+ "syscall\n"
+
+ /* if ($7 != 0)
+ * return $2;
+ */
+ "bnez $7,1f\n"
+ "bnez $2,1f\n"
+
+ /* In the child, now. Call "fn(arg)".
+ */
+ #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
+ "lw $25,0($29)\n"
+ "lw $4,4($29)\n"
+ #elif _MIPS_SIM == _MIPS_SIM_NABI32
+ "lw $25,0($29)\n"
+ "lw $4,8($29)\n"
+ #else
+ "ld $25,0($29)\n"
+ "ld $4,8($29)\n"
+ #endif
+ "jalr $25\n"
+
+ /* Call _exit($2)
+ */
+ "move $4,$2\n"
+ "li $2,%4\n"
+ "syscall\n"
+
+ "1:\n"
+ #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32
+ "addu $29, 24\n"
+ #elif _MIPS_SIM == _MIPS_SIM_NABI32
+ "add $29, 16\n"
+ #else
+ "daddu $29,16\n"
+ #endif
+ : "=&r" (__v0), "=r" (__r7)
+ : "i"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit),
+ "r"(fn), "r"(__stack), "r"(__flags), "r"(arg),
+ "r"(__ptid), "r"(__r7), "r"(__ctid)
+ : "$9", "$10", "$11", "$12", "$13", "$14", "$15",
+ "$24", "memory");
+ }
+ LSS_RETURN(int, __v0, __r7);
+ }
+ #elif defined (__PPC__)
+ #undef LSS_LOADARGS_0
+ #define LSS_LOADARGS_0(name, dummy...) \
+ __sc_0 = __NR_##name
+ #undef LSS_LOADARGS_1
+ #define LSS_LOADARGS_1(name, arg1) \
+ LSS_LOADARGS_0(name); \
+ __sc_3 = (unsigned long) (arg1)
+ #undef LSS_LOADARGS_2
+ #define LSS_LOADARGS_2(name, arg1, arg2) \
+ LSS_LOADARGS_1(name, arg1); \
+ __sc_4 = (unsigned long) (arg2)
+ #undef LSS_LOADARGS_3
+ #define LSS_LOADARGS_3(name, arg1, arg2, arg3) \
+ LSS_LOADARGS_2(name, arg1, arg2); \
+ __sc_5 = (unsigned long) (arg3)
+ #undef LSS_LOADARGS_4
+ #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4) \
+ LSS_LOADARGS_3(name, arg1, arg2, arg3); \
+ __sc_6 = (unsigned long) (arg4)
+ #undef LSS_LOADARGS_5
+ #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5) \
+ LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4); \
+ __sc_7 = (unsigned long) (arg5)
+ #undef LSS_LOADARGS_6
+ #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6) \
+ LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5); \
+ __sc_8 = (unsigned long) (arg6)
+ #undef LSS_ASMINPUT_0
+ #define LSS_ASMINPUT_0 "0" (__sc_0)
+ #undef LSS_ASMINPUT_1
+ #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3)
+ #undef LSS_ASMINPUT_2
+ #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4)
+ #undef LSS_ASMINPUT_3
+ #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5)
+ #undef LSS_ASMINPUT_4
+ #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6)
+ #undef LSS_ASMINPUT_5
+ #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7)
+ #undef LSS_ASMINPUT_6
+ #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8)
+ #undef LSS_BODY
+ #define LSS_BODY(nr, type, name, args...) \
+ long __sc_ret, __sc_err; \
+ { \
+ register unsigned long __sc_0 __asm__ ("r0"); \
+ register unsigned long __sc_3 __asm__ ("r3"); \
+ register unsigned long __sc_4 __asm__ ("r4"); \
+ register unsigned long __sc_5 __asm__ ("r5"); \
+ register unsigned long __sc_6 __asm__ ("r6"); \
+ register unsigned long __sc_7 __asm__ ("r7"); \
+ register unsigned long __sc_8 __asm__ ("r8"); \
+ \
+ LSS_LOADARGS_##nr(name, args); \
+ __asm__ __volatile__ \
+ ("sc\n\t" \
+ "mfcr %0" \
+ : "=&r" (__sc_0), \
+ "=&r" (__sc_3), "=&r" (__sc_4), \
+ "=&r" (__sc_5), "=&r" (__sc_6), \
+ "=&r" (__sc_7), "=&r" (__sc_8) \
+ : LSS_ASMINPUT_##nr \
+ : "cr0", "ctr", "memory", \
+ "r9", "r10", "r11", "r12"); \
+ __sc_ret = __sc_3; \
+ __sc_err = __sc_0; \
+ } \
+ LSS_RETURN(type, __sc_ret, __sc_err)
+ #undef _syscall0
+ #define _syscall0(type, name) \
+ type LSS_NAME(name)(void) { \
+ LSS_BODY(0, type, name); \
+ }
+ #undef _syscall1
+ #define _syscall1(type, name, type1, arg1) \
+ type LSS_NAME(name)(type1 arg1) { \
+ LSS_BODY(1, type, name, arg1); \
+ }
+ #undef _syscall2
+ #define _syscall2(type, name, type1, arg1, type2, arg2) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2) { \
+ LSS_BODY(2, type, name, arg1, arg2); \
+ }
+ #undef _syscall3
+ #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \
+ LSS_BODY(3, type, name, arg1, arg2, arg3); \
+ }
+ #undef _syscall4
+ #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+ LSS_BODY(4, type, name, arg1, arg2, arg3, arg4); \
+ }
+ #undef _syscall5
+ #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5) { \
+ LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5); \
+ }
+ #undef _syscall6
+ #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \
+ type4, arg4, type5, arg5, type6, arg6) \
+ type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \
+ type5 arg5, type6 arg6) { \
+ LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \
+ }
+ /* clone function adapted from glibc 2.3.6 clone.S */
+ /* TODO(csilvers): consider wrapping some args up in a struct, like we
+ * do for i386's _syscall6, so we can compile successfully on gcc 2.95
+ */
+ LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack,
+ int flags, void *arg, int *parent_tidptr,
+ void *newtls, int *child_tidptr) {
+ long __ret, __err;
+ {
+ register int (*__fn)(void *) __asm__ ("r8") = fn;
+ register void *__cstack __asm__ ("r4") = child_stack;
+ register int __flags __asm__ ("r3") = flags;
+ register void * __arg __asm__ ("r9") = arg;
+ register int * __ptidptr __asm__ ("r5") = parent_tidptr;
+ register void * __newtls __asm__ ("r6") = newtls;
+ register int * __ctidptr __asm__ ("r7") = child_tidptr;
+ __asm__ __volatile__(
+ /* check for fn == NULL
+ * and child_stack == NULL
+ */
+ "cmpwi cr0, %6, 0\n\t"
+ "cmpwi cr1, %7, 0\n\t"
+ "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t"
+ "beq- cr0, 1f\n\t"
+
+ /* set up stack frame for child */
+ "clrrwi %7, %7, 4\n\t"
+ "li 0, 0\n\t"
+ "stwu 0, -16(%7)\n\t"
+
+ /* fn, arg, child_stack are saved across the syscall: r28-30 */
+ "mr 28, %6\n\t"
+ "mr 29, %7\n\t"
+ "mr 27, %9\n\t"
+
+ /* syscall */
+ "li 0, %4\n\t"
+ /* flags already in r3
+ * child_stack already in r4
+ * ptidptr already in r5
+ * newtls already in r6
+ * ctidptr already in r7
+ */
+ "sc\n\t"
+
+ /* Test if syscall was successful */
+ "cmpwi cr1, 3, 0\n\t"
+ "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t"
+ "bne- cr1, 1f\n\t"
+
+ /* Do the function call */
+ "mtctr 28\n\t"
+ "mr 3, 27\n\t"
+ "bctrl\n\t"
+
+ /* Call _exit(r3) */
+ "li 0, %5\n\t"
+ "sc\n\t"
+
+ /* Return to parent */
+ "1:\n"
+ "mfcr %1\n\t"
+ "mr %0, 3\n\t"
+ : "=r" (__ret), "=r" (__err)
+ : "0" (-1), "1" (EINVAL),
+ "i" (__NR_clone), "i" (__NR_exit),
+ "r" (__fn), "r" (__cstack), "r" (__flags),
+ "r" (__arg), "r" (__ptidptr), "r" (__newtls),
+ "r" (__ctidptr)
+ : "cr0", "cr1", "memory", "ctr",
+ "r0", "r29", "r27", "r28");
+ }
+ LSS_RETURN(int, __ret, __err);
+ }
+ #endif
+ #define __NR__exit __NR_exit
+ #define __NR__gettid __NR_gettid
+ #define __NR__mremap __NR_mremap
+ LSS_INLINE _syscall1(int, chdir, const char *,p)
+ LSS_INLINE _syscall1(int, close, int, f)
+ LSS_INLINE _syscall1(int, dup, int, f)
+ LSS_INLINE _syscall2(int, dup2, int, s,
+ int, d)
+ LSS_INLINE _syscall3(int, execve, const char*, f,
+ const char*const*,a,const char*const*, e)
+ LSS_INLINE _syscall1(int, _exit, int, e)
+ LSS_INLINE _syscall3(int, fcntl, int, f,
+ int, c, long, a)
+ LSS_INLINE _syscall0(pid_t, fork)
+ LSS_INLINE _syscall2(int, fstat, int, f,
+ struct kernel_stat*, b)
+ LSS_INLINE _syscall2(int, fstatfs, int, f,
+ struct kernel_statfs*, b)
+ LSS_INLINE _syscall4(int, futex, int*, a,
+ int, o, int, v,
+ struct kernel_timespec*, t)
+ LSS_INLINE _syscall3(int, getdents, int, f,
+ struct kernel_dirent*, d, int, c)
+ LSS_INLINE _syscall3(int, getdents64, int, f,
+ struct kernel_dirent64*, d, int, c)
+ LSS_INLINE _syscall0(gid_t, getegid)
+ LSS_INLINE _syscall0(uid_t, geteuid)
+ LSS_INLINE _syscall0(pid_t, getpgrp)
+ LSS_INLINE _syscall0(pid_t, getpid)
+ LSS_INLINE _syscall0(pid_t, getppid)
+ LSS_INLINE _syscall2(int, getpriority, int, a,
+ int, b)
+ LSS_INLINE _syscall2(int, getrlimit, int, r,
+ struct kernel_rlimit*, l)
+ LSS_INLINE _syscall1(pid_t, getsid, pid_t, p)
+ LSS_INLINE _syscall0(pid_t, _gettid)
+ LSS_INLINE _syscall5(int, setxattr, const char *,p,
+ const char *, n, const void *,v,
+ size_t, s, int, f)
+ LSS_INLINE _syscall5(int, lsetxattr, const char *,p,
+ const char *, n, const void *,v,
+ size_t, s, int, f)
+ LSS_INLINE _syscall4(ssize_t, getxattr, const char *,p,
+ const char *, n, void *, v, size_t, s)
+ LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *,p,
+ const char *, n, void *, v, size_t, s)
+ LSS_INLINE _syscall2(int, kill, pid_t, p,
+ int, s)
+ LSS_INLINE _syscall3(off_t, lseek, int, f,
+ off_t, o, int, w)
+ LSS_INLINE _syscall2(int, munmap, void*, s,
+ size_t, l)
+ LSS_INLINE _syscall6(long, move_pages, pid_t, p,
+ unsigned long, n, void **,g, int *, d,
+ int *, s, int, f)
+ LSS_INLINE _syscall5(void*, _mremap, void*, o,
+ size_t, os, size_t, ns,
+ unsigned long, f, void *, a)
+ LSS_INLINE _syscall3(int, open, const char*, p,
+ int, f, int, m)
+ LSS_INLINE _syscall3(int, poll, struct kernel_pollfd*, u,
+ unsigned int, n, int, t)
+ LSS_INLINE _syscall2(int, prctl, int, o,
+ long, a)
+ LSS_INLINE _syscall4(long, ptrace, int, r,
+ pid_t, p, void *, a, void *, d)
+ LSS_INLINE _syscall3(ssize_t, read, int, f,
+ void *, b, size_t, c)
+ LSS_INLINE _syscall3(int, readlink, const char*, p,
+ char*, b, size_t, s)
+ LSS_INLINE _syscall4(int, rt_sigaction, int, s,
+ const struct kernel_sigaction*, a,
+ struct kernel_sigaction*, o, size_t, c)
+ LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s,
+ size_t, c)
+ LSS_INLINE _syscall4(int, rt_sigprocmask, int, h,
+ const struct kernel_sigset_t*, s,
+ struct kernel_sigset_t*, o, size_t, c);
+ LSS_INLINE _syscall2(int, rt_sigsuspend,
+ const struct kernel_sigset_t*, s, size_t, c);
+ LSS_INLINE _syscall3(int, sched_getaffinity,pid_t, p,
+ unsigned int, l, unsigned long *, m)
+ LSS_INLINE _syscall3(int, sched_setaffinity,pid_t, p,
+ unsigned int, l, unsigned long *, m)
+ LSS_INLINE _syscall0(int, sched_yield)
+ LSS_INLINE _syscall1(long, set_tid_address, int *, t)
+ LSS_INLINE _syscall1(int, setfsgid, gid_t, g)
+ LSS_INLINE _syscall1(int, setfsuid, uid_t, u)
+ LSS_INLINE _syscall2(int, setpgid, pid_t, p,
+ pid_t, g)
+ LSS_INLINE _syscall3(int, setpriority, int, a,
+ int, b, int, p)
+ LSS_INLINE _syscall3(int, setresgid, gid_t, r,
+ gid_t, e, gid_t, s)
+ LSS_INLINE _syscall3(int, setresuid, uid_t, r,
+ uid_t, e, uid_t, s)
+ LSS_INLINE _syscall2(int, setrlimit, int, r,
+ const struct kernel_rlimit*, l)
+ LSS_INLINE _syscall0(pid_t, setsid)
+ LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s,
+ const stack_t*, o)
+ LSS_INLINE _syscall2(int, stat, const char*, f,
+ struct kernel_stat*, b)
+ LSS_INLINE _syscall2(int, statfs, const char*, f,
+ struct kernel_statfs*, b)
+ LSS_INLINE _syscall1(int, unlink, const char*, f)
+ LSS_INLINE _syscall3(ssize_t, write, int, f,
+ const void *, b, size_t, c)
+ LSS_INLINE _syscall3(ssize_t, writev, int, f,
+ const struct kernel_iovec*, v, size_t, c)
+ #if defined(__x86_64__) || \
+ (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32)
+ LSS_INLINE _syscall3(int, recvmsg, int, s,
+ struct kernel_msghdr*, m, int, f)
+ LSS_INLINE _syscall3(int, sendmsg, int, s,
+ const struct kernel_msghdr*, m, int, f)
+ LSS_INLINE _syscall6(int, sendto, int, s,
+ const void*, m, size_t, l,
+ int, f,
+ const struct kernel_sockaddr*, a, int, t)
+ LSS_INLINE _syscall2(int, shutdown, int, s,
+ int, h)
+ LSS_INLINE _syscall3(int, socket, int, d,
+ int, t, int, p)
+ LSS_INLINE _syscall4(int, socketpair, int, d,
+ int, t, int, p, int*, s)
+ #endif
+ #if defined(__x86_64__)
+ LSS_INLINE _syscall6(void*, mmap, void*, s,
+ size_t, l, int, p,
+ int, f, int, d,
+ __off64_t, o)
+ LSS_INLINE _syscall4(int, newfstatat, int, d,
+ const char *, p,
+ struct kernel_stat*, b, int, f)
+
+ LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) {
+ return LSS_NAME(setfsgid)(gid);
+ }
+
+ LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) {
+ return LSS_NAME(setfsuid)(uid);
+ }
+
+ LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) {
+ return LSS_NAME(setresgid)(rgid, egid, sgid);
+ }
+
+ LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) {
+ return LSS_NAME(setresuid)(ruid, euid, suid);
+ }
+
+ LSS_INLINE int LSS_NAME(sigaction)(int signum,
+ const struct kernel_sigaction *act,
+ struct kernel_sigaction *oldact) {
+ /* On x86_64, the kernel requires us to always set our own
+ * SA_RESTORER in order to be able to return from a signal handler.
+ * This function must have a "magic" signature that the "gdb"
+ * (and maybe the kernel?) can recognize.
+ */
+ if (act != NULL && !(act->sa_flags & SA_RESTORER)) {
+ struct kernel_sigaction a = *act;
+ a.sa_flags |= SA_RESTORER;
+ a.sa_restorer = LSS_NAME(restore_rt)();
+ return LSS_NAME(rt_sigaction)(signum, &a, oldact,
+ (KERNEL_NSIG+7)/8);
+ } else {
+ return LSS_NAME(rt_sigaction)(signum, act, oldact,
+ (KERNEL_NSIG+7)/8);
+ }
+ }
+
+ LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) {
+ return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8);
+ }
+
+ LSS_INLINE int LSS_NAME(sigprocmask)(int how,
+ const struct kernel_sigset_t *set,
+ struct kernel_sigset_t *oldset) {
+ return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8);
+ }
+
+ LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) {
+ return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8);
+ }
+ #endif
+ #if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
+ (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32)
+ LSS_INLINE _syscall4(pid_t, wait4, pid_t, p,
+ int*, s, int, o,
+ struct kernel_rusage*, r)
+
+ LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options){
+ return LSS_NAME(wait4)(pid, status, options, 0);
+ }
+ #endif
+ #if defined(__i386__) || defined(__x86_64__)
+ LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m)
+ LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f)
+ #endif
+ #if defined(__i386__) || defined(__ARM_ARCH_3__)
+ #define __NR__setfsgid32 __NR_setfsgid32
+ #define __NR__setfsuid32 __NR_setfsuid32
+ #define __NR__setresgid32 __NR_setresgid32
+ #define __NR__setresuid32 __NR_setresuid32
+ LSS_INLINE _syscall2(int, ugetrlimit, int, r,
+ struct kernel_rlimit*, l)
+ LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f)
+ LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f)
+ LSS_INLINE _syscall3(int, _setresgid32, gid_t, r,
+ gid_t, e, gid_t, s)
+ LSS_INLINE _syscall3(int, _setresuid32, uid_t, r,
+ uid_t, e, uid_t, s)
+
+ LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) {
+ int rc;
+ if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 &&
+ LSS_ERRNO == ENOSYS) {
+ if ((unsigned int)gid & ~0xFFFFu) {
+ rc = EINVAL;
+ } else {
+ rc = LSS_NAME(setfsgid)(gid);
+ }
+ }
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) {
+ int rc;
+ if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 &&
+ LSS_ERRNO == ENOSYS) {
+ if ((unsigned int)uid & ~0xFFFFu) {
+ rc = EINVAL;
+ } else {
+ rc = LSS_NAME(setfsuid)(uid);
+ }
+ }
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) {
+ int rc;
+ if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 &&
+ LSS_ERRNO == ENOSYS) {
+ if ((unsigned int)rgid & ~0xFFFFu ||
+ (unsigned int)egid & ~0xFFFFu ||
+ (unsigned int)sgid & ~0xFFFFu) {
+ rc = EINVAL;
+ } else {
+ rc = LSS_NAME(setresgid)(rgid, egid, sgid);
+ }
+ }
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) {
+ int rc;
+ if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 &&
+ LSS_ERRNO == ENOSYS) {
+ if ((unsigned int)ruid & ~0xFFFFu ||
+ (unsigned int)euid & ~0xFFFFu ||
+ (unsigned int)suid & ~0xFFFFu) {
+ rc = EINVAL;
+ } else {
+ rc = LSS_NAME(setresuid)(ruid, euid, suid);
+ }
+ }
+ return rc;
+ }
+ #endif
+ LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) {
+ memset(&set->sig, 0, sizeof(set->sig));
+ return 0;
+ }
+
+ LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) {
+ memset(&set->sig, -1, sizeof(set->sig));
+ return 0;
+ }
+
+ LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set,
+ int signum) {
+ if (signum < 1 || signum > (int)(8*sizeof(set->sig))) {
+ LSS_ERRNO = EINVAL;
+ return -1;
+ } else {
+ set->sig[(signum - 1)/(8*sizeof(set->sig[0]))]
+ |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0])));
+ return 0;
+ }
+ }
+
+ LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set,
+ int signum) {
+ if (signum < 1 || signum > (int)(8*sizeof(set->sig))) {
+ LSS_ERRNO = EINVAL;
+ return -1;
+ } else {
+ set->sig[(signum - 1)/(8*sizeof(set->sig[0]))]
+ &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0]))));
+ return 0;
+ }
+ }
+
+ LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set,
+ int signum) {
+ if (signum < 1 || signum > (int)(8*sizeof(set->sig))) {
+ LSS_ERRNO = EINVAL;
+ return -1;
+ } else {
+ return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] &
+ (1UL << ((signum - 1) % (8*sizeof(set->sig[0])))));
+ }
+ }
+ #if defined(__i386__) || defined(__ARM_ARCH_3__) || \
+ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__)
+ #define __NR__sigaction __NR_sigaction
+ #define __NR__sigpending __NR_sigpending
+ #define __NR__sigprocmask __NR_sigprocmask
+ #define __NR__sigsuspend __NR_sigsuspend
+ #define __NR__socketcall __NR_socketcall
+ LSS_INLINE _syscall2(int, fstat64, int, f,
+ struct kernel_stat64 *, b)
+ LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo,
+ loff_t *, res, uint, wh)
+ LSS_INLINE _syscall1(void*, mmap, void*, a)
+ LSS_INLINE _syscall6(void*, mmap2, void*, s,
+ size_t, l, int, p,
+ int, f, int, d,
+ __off64_t, o)
+ LSS_INLINE _syscall3(int, _sigaction, int, s,
+ const struct kernel_old_sigaction*, a,
+ struct kernel_old_sigaction*, o)
+ LSS_INLINE _syscall1(int, _sigpending, unsigned long*, s)
+ LSS_INLINE _syscall3(int, _sigprocmask, int, h,
+ const unsigned long*, s,
+ unsigned long*, o)
+ #ifdef __PPC__
+ LSS_INLINE _syscall1(int, _sigsuspend, unsigned long, s)
+ #else
+ LSS_INLINE _syscall3(int, _sigsuspend, const void*, a,
+ int, b,
+ unsigned long, s)
+ #endif
+ LSS_INLINE _syscall2(int, stat64, const char *, p,
+ struct kernel_stat64 *, b)
+
+ LSS_INLINE int LSS_NAME(sigaction)(int signum,
+ const struct kernel_sigaction *act,
+ struct kernel_sigaction *oldact) {
+ int old_errno = LSS_ERRNO;
+ int rc;
+ struct kernel_sigaction a;
+ if (act != NULL) {
+ a = *act;
+ #ifdef __i386__
+ /* On i386, the kernel requires us to always set our own
+ * SA_RESTORER when using realtime signals. Otherwise, it does not
+ * know how to return from a signal handler. This function must have
+ * a "magic" signature that the "gdb" (and maybe the kernel?) can
+ * recognize.
+ * Apparently, a SA_RESTORER is implicitly set by the kernel, when
+ * using non-realtime signals.
+ *
+ * TODO: Test whether ARM needs a restorer
+ */
+ if (!(a.sa_flags & SA_RESTORER)) {
+ a.sa_flags |= SA_RESTORER;
+ a.sa_restorer = (a.sa_flags & SA_SIGINFO)
+ ? LSS_NAME(restore_rt)() : LSS_NAME(restore)();
+ }
+ #endif
+ }
+ rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact,
+ (KERNEL_NSIG+7)/8);
+ if (rc < 0 && LSS_ERRNO == ENOSYS) {
+ struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa;
+ if (!act) {
+ ptr_a = NULL;
+ } else {
+ oa.sa_handler_ = act->sa_handler_;
+ memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask));
+ #ifndef __mips__
+ oa.sa_restorer = act->sa_restorer;
+ #endif
+ oa.sa_flags = act->sa_flags;
+ }
+ if (!oldact) {
+ ptr_oa = NULL;
+ }
+ LSS_ERRNO = old_errno;
+ rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa);
+ if (rc == 0 && oldact) {
+ if (act) {
+ memcpy(oldact, act, sizeof(*act));
+ } else {
+ memset(oldact, 0, sizeof(*oldact));
+ }
+ oldact->sa_handler_ = ptr_oa->sa_handler_;
+ oldact->sa_flags = ptr_oa->sa_flags;
+ memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask));
+ #ifndef __mips__
+ oldact->sa_restorer = ptr_oa->sa_restorer;
+ #endif
+ }
+ }
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) {
+ int old_errno = LSS_ERRNO;
+ int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8);
+ if (rc < 0 && LSS_ERRNO == ENOSYS) {
+ LSS_ERRNO = old_errno;
+ LSS_NAME(sigemptyset)(set);
+ rc = LSS_NAME(_sigpending)(&set->sig[0]);
+ }
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(sigprocmask)(int how,
+ const struct kernel_sigset_t *set,
+ struct kernel_sigset_t *oldset) {
+ int olderrno = LSS_ERRNO;
+ int rc = LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8);
+ if (rc < 0 && LSS_ERRNO == ENOSYS) {
+ LSS_ERRNO = olderrno;
+ if (oldset) {
+ LSS_NAME(sigemptyset)(oldset);
+ }
+ rc = LSS_NAME(_sigprocmask)(how,
+ set ? &set->sig[0] : NULL,
+ oldset ? &oldset->sig[0] : NULL);
+ }
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) {
+ int olderrno = LSS_ERRNO;
+ int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8);
+ if (rc < 0 && LSS_ERRNO == ENOSYS) {
+ LSS_ERRNO = olderrno;
+ rc = LSS_NAME(_sigsuspend)(
+ #ifndef __PPC__
+ set, 0,
+ #endif
+ set->sig[0]);
+ }
+ return rc;
+ }
+ #endif
+ #if defined(__PPC__)
+ #undef LSS_SC_LOADARGS_0
+ #define LSS_SC_LOADARGS_0(dummy...)
+ #undef LSS_SC_LOADARGS_1
+ #define LSS_SC_LOADARGS_1(arg1) \
+ __sc_4 = (unsigned long) (arg1)
+ #undef LSS_SC_LOADARGS_2
+ #define LSS_SC_LOADARGS_2(arg1, arg2) \
+ LSS_SC_LOADARGS_1(arg1); \
+ __sc_5 = (unsigned long) (arg2)
+ #undef LSS_SC_LOADARGS_3
+ #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \
+ LSS_SC_LOADARGS_2(arg1, arg2); \
+ __sc_6 = (unsigned long) (arg3)
+ #undef LSS_SC_LOADARGS_4
+ #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \
+ LSS_SC_LOADARGS_3(arg1, arg2, arg3); \
+ __sc_7 = (unsigned long) (arg4)
+ #undef LSS_SC_LOADARGS_5
+ #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \
+ LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \
+ __sc_8 = (unsigned long) (arg5)
+ #undef LSS_SC_BODY
+ #define LSS_SC_BODY(nr, type, opt, args...) \
+ long __sc_ret, __sc_err; \
+ { \
+ register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \
+ register unsigned long __sc_3 __asm__ ("r3") = opt; \
+ register unsigned long __sc_4 __asm__ ("r4"); \
+ register unsigned long __sc_5 __asm__ ("r5"); \
+ register unsigned long __sc_6 __asm__ ("r6"); \
+ register unsigned long __sc_7 __asm__ ("r7"); \
+ register unsigned long __sc_8 __asm__ ("r8"); \
+ LSS_SC_LOADARGS_##nr(args); \
+ __asm__ __volatile__ \
+ ("stwu 1, -48(1)\n\t" \
+ "stw 4, 20(1)\n\t" \
+ "stw 5, 24(1)\n\t" \
+ "stw 6, 28(1)\n\t" \
+ "stw 7, 32(1)\n\t" \
+ "stw 8, 36(1)\n\t" \
+ "addi 4, 1, 20\n\t" \
+ "sc\n\t" \
+ "mfcr %0" \
+ : "=&r" (__sc_0), \
+ "=&r" (__sc_3), "=&r" (__sc_4), \
+ "=&r" (__sc_5), "=&r" (__sc_6), \
+ "=&r" (__sc_7), "=&r" (__sc_8) \
+ : LSS_ASMINPUT_##nr \
+ : "cr0", "ctr", "memory"); \
+ __sc_ret = __sc_3; \
+ __sc_err = __sc_0; \
+ } \
+ LSS_RETURN(type, __sc_ret, __sc_err)
+
+ LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
+ int flags){
+ LSS_SC_BODY(3, ssize_t, 17, s, msg, flags);
+ }
+
+ LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
+ const struct kernel_msghdr *msg,
+ int flags) {
+ LSS_SC_BODY(3, ssize_t, 16, s, msg, flags);
+ }
+
+ // TODO(csilvers): why is this ifdef'ed out?
+#if 0
+ LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
+ int flags,
+ const struct kernel_sockaddr *to,
+ unsigned int tolen) {
+ LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen);
+ }
+#endif
+
+ LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
+ LSS_SC_BODY(2, int, 13, s, how);
+ }
+
+ LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
+ LSS_SC_BODY(3, int, 1, domain, type, protocol);
+ }
+
+ LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol,
+ int sv[2]) {
+ LSS_SC_BODY(4, int, 8, d, type, protocol, sv);
+ }
+ #endif
+ #if defined(__i386__) || defined(__ARM_ARCH_3__) || \
+ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
+ #define __NR__socketcall __NR_socketcall
+ LSS_INLINE _syscall2(int, _socketcall, int, c,
+ va_list, a)
+
+ LSS_INLINE int LSS_NAME(socketcall)(int op, ...) {
+ int rc;
+ va_list ap;
+ va_start(ap, op);
+ rc = LSS_NAME(_socketcall)(op, ap);
+ va_end(ap);
+ return rc;
+ }
+
+ LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg,
+ int flags){
+ return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags);
+ }
+
+ LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s,
+ const struct kernel_msghdr *msg,
+ int flags) {
+ return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags);
+ }
+
+ LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len,
+ int flags,
+ const struct kernel_sockaddr *to,
+ unsigned int tolen) {
+ return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen);
+ }
+
+ LSS_INLINE int LSS_NAME(shutdown)(int s, int how) {
+ return LSS_NAME(socketcall)(13, s, how);
+ }
+
+ LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) {
+ return LSS_NAME(socketcall)(1, domain, type, protocol);
+ }
+
+ LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol,
+ int sv[2]) {
+ return LSS_NAME(socketcall)(8, d, type, protocol, sv);
+ }
+ #endif
+ #if defined(__i386__) || defined(__PPC__)
+ LSS_INLINE _syscall4(int, fstatat64, int, d,
+ const char *, p,
+ struct kernel_stat64 *, b, int, f)
+ #endif
+ #if defined(__i386__) || defined(__PPC__) || \
+ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32)
+ LSS_INLINE _syscall3(pid_t, waitpid, pid_t, p,
+ int*, s, int, o)
+ #endif
+ #if defined(__mips__)
+ /* sys_pipe() on MIPS has non-standard calling conventions, as it returns
+ * both file handles through CPU registers.
+ */
+ LSS_INLINE int LSS_NAME(pipe)(int *p) {
+ register unsigned long __v0 __asm__("$2") = __NR_pipe;
+ register unsigned long __v1 __asm__("$3");
+ register unsigned long __r7 __asm__("$7");
+ __asm__ __volatile__ ("syscall\n"
+ : "=&r"(__v0), "=&r"(__v1), "+r" (__r7)
+ : "0"(__v0)
+ : "$8", "$9", "$10", "$11", "$12",
+ "$13", "$14", "$15", "$24", "memory");
+ if (__r7) {
+ LSS_ERRNO = __v0;
+ return -1;
+ } else {
+ p[0] = __v0;
+ p[1] = __v1;
+ return 0;
+ }
+ }
+ #else
+ LSS_INLINE _syscall1(int, pipe, int *, p)
+ #endif
+ /* TODO(csilvers): see if ppc can/should support this as well */
+ #if defined(__i386__) || defined(__ARM_ARCH_3__) || \
+ (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64)
+ #define __NR__statfs64 __NR_statfs64
+ #define __NR__fstatfs64 __NR_fstatfs64
+ LSS_INLINE _syscall3(int, _statfs64, const char*, p,
+ size_t, s,struct kernel_statfs64*, b)
+ LSS_INLINE _syscall3(int, _fstatfs64, int, f,
+ size_t, s,struct kernel_statfs64*, b)
+ LSS_INLINE int LSS_NAME(statfs64)(const char *p,
+ struct kernel_statfs64 *b) {
+ return LSS_NAME(_statfs64)(p, sizeof(*b), b);
+ }
+ LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) {
+ return LSS_NAME(_fstatfs64)(f, sizeof(*b), b);
+ }
+ #endif
+
+ LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) {
+ extern char **environ;
+ return LSS_NAME(execve)(path, argv, (const char *const *)environ);
+ }
+
+ LSS_INLINE pid_t LSS_NAME(gettid)() {
+ pid_t tid = LSS_NAME(_gettid)();
+ if (tid != -1) {
+ return tid;
+ }
+ return LSS_NAME(getpid)();
+ }
+
+ LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size,
+ size_t new_size, int flags, ...) {
+ va_list ap;
+ void *new_address, *rc;
+ va_start(ap, flags);
+ new_address = va_arg(ap, void *);
+ rc = LSS_NAME(_mremap)(old_address, old_size, new_size,
+ flags, new_address);
+ va_end(ap);
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) {
+ /* PTRACE_DETACH can sometimes forget to wake up the tracee and it
+ * then sends job control signals to the real parent, rather than to
+ * the tracer. We reduce the risk of this happening by starting a
+ * whole new time slice, and then quickly sending a SIGCONT signal
+ * right after detaching from the tracee.
+ */
+ int rc, err;
+ LSS_NAME(sched_yield)();
+ rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0);
+ err = LSS_ERRNO;
+ LSS_NAME(kill)(pid, SIGCONT);
+ LSS_ERRNO = err;
+ return rc;
+ }
+
+ LSS_INLINE int LSS_NAME(raise)(int sig) {
+ return LSS_NAME(kill)(LSS_NAME(getpid)(), sig);
+ }
+
+ LSS_INLINE int LSS_NAME(setpgrp)() {
+ return LSS_NAME(setpgid)(0, 0);
+ }
+
+ LSS_INLINE int LSS_NAME(sysconf)(int name) {
+ extern int __getpagesize(void);
+ switch (name) {
+ case _SC_OPEN_MAX: {
+ struct kernel_rlimit limit;
+ return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0
+ ? 8192 : limit.rlim_cur;
+ }
+ case _SC_PAGESIZE:
+ return __getpagesize();
+ default:
+ errno = ENOSYS;
+ return -1;
+ }
+ }
+ #if defined(__x86_64__) || \
+ (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64)
+ /* pread64() and pwrite64() do not exist on 64-bit systems... */
+ LSS_INLINE _syscall3(int, readahead, int, f,
+ loff_t, o, unsigned, c)
+ #else
+ #define __NR__pread64 __NR_pread64
+ #define __NR__pwrite64 __NR_pwrite64
+ #define __NR__readahead __NR_readahead
+ LSS_INLINE _syscall5(ssize_t, _pread64, int, f,
+ void *, b, size_t, c, unsigned, o1,
+ unsigned, o2)
+ LSS_INLINE _syscall5(ssize_t, _pwrite64, int, f,
+ const void *, b, size_t, c, unsigned, o1,
+ long, o2)
+ LSS_INLINE _syscall4(int, _readahead, int, f,
+ unsigned, o1, unsigned, o2, size_t, c);
+ /* We force 64bit-wide parameters onto the stack, then access each
+ * 32-bit component individually. This guarantees that we build the
+ * correct parameters independent of the native byte-order of the
+ * underlying architecture.
+ */
+ LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count,
+ loff_t off) {
+ union { loff_t off; unsigned arg[2]; } o = { off };
+ return LSS_NAME(_pread64)(fd, buf, count, o.arg[0], o.arg[1]);
+ }
+ LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf,
+ size_t count, loff_t off) {
+ union { loff_t off; unsigned arg[2]; } o = { off };
+ return LSS_NAME(_pwrite64)(fd, buf, count, o.arg[0], o.arg[1]);
+ }
+ LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) {
+ union { loff_t off; unsigned arg[2]; } o = { off };
+ return LSS_NAME(_readahead)(fd, o.arg[0], o.arg[1], len);
+ }
+ #endif
+#endif
+
+#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS)
+}
+#endif
+
+#endif
+#endif
diff --git a/breakpad/linux/memory.h b/breakpad/linux/memory.h
new file mode 100644
index 0000000..e6947a1
--- /dev/null
+++ b/breakpad/linux/memory.h
@@ -0,0 +1,176 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_HANDLER_MEMORY_H_
+#define CLIENT_LINUX_HANDLER_MEMORY_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "breakpad/linux/linux_syscall_support.h"
+
+namespace google_breakpad {
+
+// This is very simple allocator which fetches pages from the kernel directly.
+// Thus, it can be used even when the heap may be corrupted.
+//
+// There is no free operation. The pages are only freed when the object is
+// destroyed.
+class PageAllocator {
+ public:
+ PageAllocator()
+ : page_size_(getpagesize()),
+ last_(NULL),
+ current_page_(NULL),
+ page_offset_(0) {
+ }
+
+ ~PageAllocator() {
+ FreeAll();
+ }
+
+ void *Alloc(unsigned bytes) {
+ if (!bytes)
+ return NULL;
+
+ if (current_page_ && page_size_ - page_offset_ >= bytes) {
+ uint8_t *const ret = current_page_ + page_offset_;
+ page_offset_ += bytes;
+ if (page_offset_ == page_size_) {
+ page_offset_ = 0;
+ current_page_ = NULL;
+ }
+
+ return ret;
+ }
+
+ const unsigned pages =
+ (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_;
+ uint8_t *const ret = GetNPages(pages);
+ if (!ret)
+ return NULL;
+
+ page_offset_ = (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % page_size_;
+ current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL;
+
+ return ret + sizeof(PageHeader);
+ }
+
+ private:
+ uint8_t *GetNPages(unsigned num_pages) {
+ void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (a == MAP_FAILED)
+ return NULL;
+
+ struct PageHeader *header = reinterpret_cast<PageHeader*>(a);
+ header->next = last_;
+ header->num_pages = num_pages;
+ last_ = header;
+
+ return reinterpret_cast<uint8_t*>(a);
+ }
+
+ void FreeAll() {
+ PageHeader *next;
+
+ for (PageHeader *cur = last_; cur; cur = next) {
+ next = cur->next;
+ sys_munmap(cur, cur->num_pages * page_size_);
+ }
+ }
+
+ struct PageHeader {
+ PageHeader *next; // pointer to the start of the next set of pages.
+ unsigned num_pages; // the number of pages in this set.
+ };
+
+ const unsigned page_size_;
+ PageHeader *last_;
+ uint8_t *current_page_;
+ unsigned page_offset_;
+};
+
+// A wasteful vector is like a normal std::vector, except that it's very much
+// simplier and it allocates memory from a PageAllocator. It's wasteful
+// because, when resizing, it always allocates a whole new array since the
+// PageAllocator doesn't support realloc.
+template<class T>
+class wasteful_vector {
+ public:
+ wasteful_vector(PageAllocator *allocator, unsigned size_hint = 16)
+ : allocator_(allocator),
+ a_((T*) allocator->Alloc(sizeof(T) * size_hint)),
+ allocated_(size_hint),
+ used_(0) {
+ }
+
+ void push_back(const T& new_element) {
+ if (used_ == allocated_)
+ Realloc(allocated_ * 2);
+ a_[used_++] = new_element;
+ }
+
+ size_t size() const {
+ return used_;
+ }
+
+ T& operator[](size_t index) {
+ return a_[index];
+ }
+
+ const T& operator[](size_t index) const {
+ return a_[index];
+ }
+
+ private:
+ void Realloc(unsigned new_size) {
+ T *new_array =
+ reinterpret_cast<T*>(allocator_->Alloc(sizeof(T) * new_size));
+ memcpy(new_array, a_, used_ * sizeof(T));
+ a_ = new_array;
+ allocated_ = new_size;
+ }
+
+ PageAllocator *const allocator_;
+ T *a_; // pointer to an array of |allocated_| elements.
+ unsigned allocated_; // size of |a_|, in elements.
+ unsigned used_; // number of used slots in |a_|.
+};
+
+} // namespace google_breakpad
+
+inline void* operator new(size_t nbytes,
+ google_breakpad::PageAllocator& allocator) {
+ return allocator.Alloc(nbytes);
+}
+
+#endif // CLIENT_LINUX_HANDLER_MEMORY_H_
diff --git a/breakpad/linux/memory_unittest.cc b/breakpad/linux/memory_unittest.cc
new file mode 100644
index 0000000..22b6439
--- /dev/null
+++ b/breakpad/linux/memory_unittest.cc
@@ -0,0 +1,84 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "breakpad/linux/memory.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test PageAllocatorTest;
+}
+
+TEST(PageAllocatorTest, Setup) {
+ PageAllocator allocator;
+}
+
+TEST(PageAllocatorTest, SmallObjects) {
+ PageAllocator allocator;
+
+ for (unsigned i = 1; i < 1024; ++i) {
+ uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i));
+ ASSERT_FALSE(p == NULL);
+ memset(p, 0, i);
+ }
+}
+
+TEST(PageAllocatorTest, LargeObject) {
+ PageAllocator allocator;
+
+ uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(10000));
+ ASSERT_FALSE(p == NULL);
+ for (unsigned i = 1; i < 10; ++i) {
+ uint8_t *p = reinterpret_cast<uint8_t*>(allocator.Alloc(i));
+ ASSERT_FALSE(p == NULL);
+ memset(p, 0, i);
+ }
+}
+
+namespace {
+typedef testing::Test WastefulVectorTest;
+}
+
+TEST(WastefulVectorTest, Setup) {
+ PageAllocator allocator_;
+ wasteful_vector<int> v(&allocator_);
+ ASSERT_EQ(v.size(), 0u);
+}
+
+TEST(WastefulVectorTest, Simple) {
+ PageAllocator allocator_;
+ wasteful_vector<int> v(&allocator_);
+
+ for (unsigned i = 0; i < 256; ++i)
+ v.push_back(i);
+ ASSERT_EQ(v.size(), 256u);
+ for (unsigned i = 0; i < 256; ++i)
+ ASSERT_EQ(v[i], i);
+}
diff --git a/breakpad/linux/minidump-2-core.cc b/breakpad/linux/minidump-2-core.cc
new file mode 100644
index 0000000..095ae5f
--- /dev/null
+++ b/breakpad/linux/minidump-2-core.cc
@@ -0,0 +1,601 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Converts a minidump file to a core file which gdb can read.
+// Large parts lifted from the userspace core dumper:
+// http://code.google.com/p/google-coredumper/
+//
+// Usage: minidump-2-core 1234.dmp > core
+
+#include <vector>
+
+#include <stdio.h>
+
+#include <elf.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/user.h>
+#include <sys/mman.h>
+
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/common/minidump_cpu_x86.h"
+#include "breakpad/linux/minidump_format_linux.h"
+
+#if __WORDSIZE == 64
+ #define ELF_CLASS ELFCLASS64
+ #define Ehdr Elf64_Ehdr
+ #define Phdr Elf64_Phdr
+ #define Shdr Elf64_Shdr
+ #define Nhdr Elf64_Nhdr
+ #define auxv_t Elf64_auxv_t
+#else
+ #define ELF_CLASS ELFCLASS32
+ #define Ehdr Elf32_Ehdr
+ #define Phdr Elf32_Phdr
+ #define Shdr Elf32_Shdr
+ #define Nhdr Elf32_Nhdr
+ #define auxv_t Elf32_auxv_t
+#endif
+
+
+#if defined(__x86_64__)
+ #define ELF_ARCH EM_X86_64
+#elif defined(__i386__)
+ #define ELF_ARCH EM_386
+#elif defined(__ARM_ARCH_3__)
+ #define ELF_ARCH EM_ARM
+#elif defined(__mips__)
+ #define ELF_ARCH EM_MIPS
+#endif
+
+static int usage(const char* argv0) {
+ fprintf(stderr, "Usage: %s <minidump file>\n", argv0);
+ return 1;
+}
+
+// Write all of the given buffer, handling short writes and EINTR. Return true
+// iff successful.
+static bool
+writea(int fd, const void* idata, size_t length) {
+ const uint8_t* data = (const uint8_t*) idata;
+
+ size_t done = 0;
+ while (done < length) {
+ ssize_t r;
+ do {
+ r = write(fd, data + done, length - done);
+ } while (r == -1 && errno == EINTR);
+
+ if (r < 1)
+ return false;
+ done += r;
+ }
+
+ return true;
+}
+
+// A range of a mmaped file.
+class MMappedRange {
+ public:
+ MMappedRange(const void* data, size_t length)
+ : data_(reinterpret_cast<const uint8_t*>(data)),
+ length_(length) {
+ }
+
+ // Get an object of |length| bytes at |offset| and return a pointer to it
+ // unless it's out of bounds.
+ const void* GetObject(size_t offset, size_t length) {
+ if (offset + length < offset)
+ return NULL;
+ if (offset + length > length_)
+ return NULL;
+ return data_ + offset;
+ }
+
+ // Get element |index| of an array of objects of length |length| starting at
+ // |offset| bytes. Return NULL if out of bounds.
+ const void* GetArrayElement(size_t offset, size_t length, unsigned index) {
+ const size_t element_offset = offset + index * length;
+ return GetObject(element_offset, length);
+ }
+
+ // Return a new range which is a subset of this range.
+ MMappedRange Subrange(const MDLocationDescriptor& location) const {
+ if (location.rva > length_ ||
+ location.rva + location.data_size < location.rva ||
+ location.rva + location.data_size > length_) {
+ return MMappedRange(NULL, 0);
+ }
+
+ return MMappedRange(data_ + location.rva, location.data_size);
+ }
+
+ const uint8_t* data() const { return data_; }
+ size_t length() const { return length_; }
+
+ private:
+ const uint8_t* const data_;
+ const size_t length_;
+};
+
+/* Dynamically determines the byte sex of the system. Returns non-zero
+ * for big-endian machines.
+ */
+static inline int sex() {
+ int probe = 1;
+ return !*(char *)&probe;
+}
+
+typedef struct elf_timeval { /* Time value with microsecond resolution */
+ long tv_sec; /* Seconds */
+ long tv_usec; /* Microseconds */
+} elf_timeval;
+
+typedef struct elf_siginfo { /* Information about signal (unused) */
+ int32_t si_signo; /* Signal number */
+ int32_t si_code; /* Extra code */
+ int32_t si_errno; /* Errno */
+} elf_siginfo;
+
+typedef struct prstatus { /* Information about thread; includes CPU reg*/
+ elf_siginfo pr_info; /* Info associated with signal */
+ uint16_t pr_cursig; /* Current signal */
+ unsigned long pr_sigpend; /* Set of pending signals */
+ unsigned long pr_sighold; /* Set of held signals */
+ pid_t pr_pid; /* Process ID */
+ pid_t pr_ppid; /* Parent's process ID */
+ pid_t pr_pgrp; /* Group ID */
+ pid_t pr_sid; /* Session ID */
+ elf_timeval pr_utime; /* User time */
+ elf_timeval pr_stime; /* System time */
+ elf_timeval pr_cutime; /* Cumulative user time */
+ elf_timeval pr_cstime; /* Cumulative system time */
+ user_regs_struct pr_reg; /* CPU registers */
+ uint32_t pr_fpvalid; /* True if math co-processor being used */
+} prstatus;
+
+typedef struct prpsinfo { /* Information about process */
+ unsigned char pr_state; /* Numeric process state */
+ char pr_sname; /* Char for pr_state */
+ unsigned char pr_zomb; /* Zombie */
+ signed char pr_nice; /* Nice val */
+ unsigned long pr_flag; /* Flags */
+#if defined(__x86_64__) || defined(__mips__)
+ uint32_t pr_uid; /* User ID */
+ uint32_t pr_gid; /* Group ID */
+#else
+ uint16_t pr_uid; /* User ID */
+ uint16_t pr_gid; /* Group ID */
+#endif
+ pid_t pr_pid; /* Process ID */
+ pid_t pr_ppid; /* Parent's process ID */
+ pid_t pr_pgrp; /* Group ID */
+ pid_t pr_sid; /* Session ID */
+ char pr_fname[16]; /* Filename of executable */
+ char pr_psargs[80]; /* Initial part of arg list */
+} prpsinfo;
+
+// We parse the minidump file and keep the parsed information in this structure.
+struct CrashedProcess {
+ CrashedProcess()
+ : crashing_tid(-1),
+ auxv(NULL),
+ auxv_length(0) {
+ memset(&prps, 0, sizeof(prps));
+ prps.pr_sname = 'R';
+ }
+
+ struct Mapping {
+ uint64_t start_address, end_address;
+ };
+ std::vector<Mapping> mappings;
+
+ pid_t crashing_tid;
+ int fatal_signal;
+
+ struct Thread {
+ pid_t tid;
+ user_regs_struct regs;
+ user_fpregs_struct fpregs;
+ user_fpxregs_struct fpxregs;
+ uintptr_t stack_addr;
+ const uint8_t* stack;
+ size_t stack_length;
+ };
+ std::vector<Thread> threads;
+
+ const uint8_t* auxv;
+ size_t auxv_length;
+
+ prpsinfo prps;
+};
+
+static uint32_t
+U32(const uint8_t* data) {
+ uint32_t v;
+ memcpy(&v, data, sizeof(v));
+ return v;
+}
+
+static uint16_t
+U16(const uint8_t* data) {
+ uint16_t v;
+ memcpy(&v, data, sizeof(v));
+ return v;
+}
+
+#if defined(__i386__)
+static void
+ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) {
+ const MDRawContextX86* rawregs =
+ (const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86));
+
+ thread->regs.ebx = rawregs->ebx;
+ thread->regs.ecx = rawregs->ecx;
+ thread->regs.edx = rawregs->edx;
+ thread->regs.esi = rawregs->esi;
+ thread->regs.edi = rawregs->edi;
+ thread->regs.ebp = rawregs->ebp;
+ thread->regs.eax = rawregs->eax;
+ thread->regs.xds = rawregs->ds;
+ thread->regs.xes = rawregs->es;
+ thread->regs.xfs = rawregs->fs;
+ thread->regs.xgs = rawregs->gs;
+ thread->regs.orig_eax = rawregs->eax;
+ thread->regs.eip = rawregs->eip;
+ thread->regs.xcs = rawregs->cs;
+ thread->regs.eflags = rawregs->eflags;
+ thread->regs.esp = rawregs->esp;
+ thread->regs.xss = rawregs->ss;
+
+ thread->fpregs.cwd = rawregs->float_save.control_word;
+ thread->fpregs.swd = rawregs->float_save.status_word;
+ thread->fpregs.twd = rawregs->float_save.tag_word;
+ thread->fpregs.fip = rawregs->float_save.error_offset;
+ thread->fpregs.fcs = rawregs->float_save.error_selector;
+ thread->fpregs.foo = rawregs->float_save.data_offset;
+ thread->fpregs.fos = rawregs->float_save.data_selector;
+ memcpy(thread->fpregs.st_space, rawregs->float_save.register_area,
+ 10 * 8);
+
+ thread->fpxregs.cwd = rawregs->float_save.control_word;
+ thread->fpxregs.swd = rawregs->float_save.status_word;
+ thread->fpxregs.twd = rawregs->float_save.tag_word;
+ thread->fpxregs.fop = U16(rawregs->extended_registers + 6);
+ thread->fpxregs.fip = U16(rawregs->extended_registers + 8);
+ thread->fpxregs.fcs = U16(rawregs->extended_registers + 12);
+ thread->fpxregs.foo = U16(rawregs->extended_registers + 16);
+ thread->fpxregs.fos = U16(rawregs->extended_registers + 20);
+ thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24);
+ memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128);
+ memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128);
+}
+#else
+#error "This code has not been ported to your platform yet"
+#endif
+
+static void
+ParseThreadList(CrashedProcess* crashinfo, MMappedRange range,
+ const MMappedRange& full_file) {
+ const uint32_t num_threads =
+ *(const uint32_t*) range.GetObject(0, sizeof(uint32_t));
+ for (unsigned i = 0; i < num_threads; ++i) {
+ CrashedProcess::Thread thread;
+ memset(&thread, 0, sizeof(thread));
+ const MDRawThread* rawthread =
+ (MDRawThread*) range.GetArrayElement(sizeof(uint32_t),
+ sizeof(MDRawThread), i);
+ thread.tid = rawthread->thread_id;
+ thread.stack_addr = rawthread->stack.start_of_memory_range;
+ MMappedRange stack_range = full_file.Subrange(rawthread->stack.memory);
+ thread.stack = stack_range.data();
+ thread.stack_length = rawthread->stack.memory.data_size;
+
+ ParseThreadRegisters(&thread,
+ full_file.Subrange(rawthread->thread_context));
+
+ crashinfo->threads.push_back(thread);
+ }
+}
+
+static void
+ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) {
+ crashinfo->auxv = range.data();
+ crashinfo->auxv_length = range.length();
+}
+
+static void
+ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) {
+ const char* cmdline = (const char*) range.data();
+ for (size_t i = 0; i < range.length(); ++i) {
+ if (cmdline[i] == 0) {
+ static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1;
+ static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1;
+ memset(crashinfo->prps.pr_fname, 0, fname_len + 1);
+ memset(crashinfo->prps.pr_psargs, 0, args_len + 1);
+ const char* binary_name = strrchr(cmdline, '/');
+ if (binary_name) {
+ binary_name++;
+ const unsigned len = strlen(binary_name);
+ memcpy(crashinfo->prps.pr_fname, binary_name,
+ len > fname_len ? fname_len : len);
+ } else {
+ memcpy(crashinfo->prps.pr_fname, cmdline,
+ i > fname_len ? fname_len : i);
+ }
+
+ const unsigned len = range.length() > args_len ?
+ args_len : range.length();
+ memcpy(crashinfo->prps.pr_psargs, cmdline, len);
+ for (unsigned i = 0; i < len; ++i) {
+ if (crashinfo->prps.pr_psargs[i] == 0)
+ crashinfo->prps.pr_psargs[i] = ' ';
+ }
+ }
+ }
+}
+
+static void
+ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) {
+ const MDRawExceptionStream* exp =
+ (MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream));
+ crashinfo->crashing_tid = exp->thread_id;
+ crashinfo->fatal_signal = (int) exp->exception_record.exception_code;
+}
+
+static bool
+WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) {
+ struct prstatus pr;
+ memset(&pr, 0, sizeof(pr));
+
+ pr.pr_info.si_signo = fatal_signal;
+ pr.pr_cursig = fatal_signal;
+ pr.pr_pid = thread.tid;
+ memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct));
+
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = sizeof(struct prstatus);
+ nhdr.n_type = NT_PRSTATUS;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &pr, sizeof(struct prstatus))) {
+ return false;
+ }
+
+ nhdr.n_descsz = sizeof(user_fpregs_struct);
+ nhdr.n_type = NT_FPREGSET;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) {
+ return false;
+ }
+
+ nhdr.n_descsz = sizeof(user_fpxregs_struct);
+ nhdr.n_type = NT_PRXFPREG;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "LINUX\0\0\0", 8) ||
+ !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) {
+ return false;
+ }
+
+ return true;
+}
+
+static void
+ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range) {
+ const uint32_t num_mappings =
+ *(const uint32_t*) range.GetObject(0, sizeof(uint32_t));
+ for (unsigned i = 0; i < num_mappings; ++i) {
+ CrashedProcess::Mapping mapping;
+ const MDRawModule* rawmodule =
+ (MDRawModule*) range.GetArrayElement(sizeof(uint32_t),
+ sizeof(MDRawModule), i);
+ mapping.start_address = rawmodule->base_of_image;
+ mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image;
+
+ crashinfo->mappings.push_back(mapping);
+ }
+}
+
+int
+main(int argc, char** argv) {
+ if (argc != 2)
+ return usage(argv[0]);
+
+ const int fd = open(argv[1], O_RDONLY);
+ if (fd < 0)
+ return usage(argv[0]);
+
+ struct stat st;
+ fstat(fd, &st);
+
+ const void* bytes = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+ close(fd);
+ if (bytes == MAP_FAILED) {
+ perror("Failed to mmap dump file");
+ return 1;
+ }
+
+ MMappedRange dump(bytes, st.st_size);
+
+ const MDRawHeader* header =
+ (const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader));
+
+ CrashedProcess crashinfo;
+
+ for (unsigned i = 0; i < header->stream_count; ++i) {
+ const MDRawDirectory* dirent =
+ (const MDRawDirectory*) dump.GetArrayElement(
+ header->stream_directory_rva, sizeof(MDRawDirectory), i);
+ switch (dirent->stream_type) {
+ case MD_THREAD_LIST_STREAM:
+ ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump);
+ break;
+ case MD_LINUX_AUXV:
+ ParseAuxVector(&crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_LINUX_CMD_LINE:
+ ParseCmdLine(&crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_EXCEPTION_STREAM:
+ ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location));
+ break;
+ case MD_MODULE_LIST_STREAM:
+ ParseModuleStream(&crashinfo, dump.Subrange(dirent->location));
+ default:
+ fprintf(stderr, "Skipping %x\n", dirent->stream_type);
+ }
+ }
+
+ // Write the ELF header. The file will look like:
+ // ELF header
+ // Phdr for the PT_NOTE
+ // Phdr for each of the thread stacks
+ // PT_NOTE
+ // each of the thread stacks
+ Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(Ehdr));
+ ehdr.e_ident[0] = ELFMAG0;
+ ehdr.e_ident[1] = ELFMAG1;
+ ehdr.e_ident[2] = ELFMAG2;
+ ehdr.e_ident[3] = ELFMAG3;
+ ehdr.e_ident[4] = ELF_CLASS;
+ ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB;
+ ehdr.e_ident[6] = EV_CURRENT;
+ ehdr.e_type = ET_CORE;
+ ehdr.e_machine = ELF_ARCH;
+ ehdr.e_version = EV_CURRENT;
+ ehdr.e_phoff = sizeof(Ehdr);
+ ehdr.e_ehsize = sizeof(Ehdr);
+ ehdr.e_phentsize= sizeof(Phdr);
+ ehdr.e_phnum = 1 + crashinfo.threads.size() + crashinfo.mappings.size();
+ ehdr.e_shentsize= sizeof(Shdr);
+ if (!writea(1, &ehdr, sizeof(Ehdr)))
+ return 1;
+
+ size_t offset = sizeof(Ehdr) +
+ (1 + crashinfo.threads.size() +
+ crashinfo.mappings.size()) * sizeof(Phdr);
+ size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) +
+ // sizeof(Nhdr) + 8 + sizeof(user) +
+ sizeof(Nhdr) + 8 + crashinfo.auxv_length +
+ crashinfo.threads.size() * (
+ (sizeof(Nhdr) + 8 + sizeof(prstatus)) +
+ sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) +
+ sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct));
+
+ Phdr phdr;
+ memset(&phdr, 0, sizeof(Phdr));
+ phdr.p_type = PT_NOTE;
+ phdr.p_offset = offset;
+ phdr.p_filesz = filesz;
+ if (!writea(1, &phdr, sizeof(phdr)))
+ return 1;
+
+ phdr.p_type = PT_LOAD;
+ phdr.p_align = getpagesize();
+ size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align);
+ if (note_align == phdr.p_align)
+ note_align = 0;
+ offset += note_align;
+
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ const CrashedProcess::Thread& thread = crashinfo.threads[i];
+ offset += filesz;
+ filesz = thread.stack_length;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = thread.stack_addr;
+ phdr.p_filesz = phdr.p_memsz = filesz;
+ phdr.p_flags = PF_R | PF_W;
+ if (!writea(1, &phdr, sizeof(phdr)))
+ return 1;
+ }
+
+ for (unsigned i = 0; i < crashinfo.mappings.size(); ++i) {
+ const CrashedProcess::Mapping& mapping = crashinfo.mappings[i];
+ phdr.p_offset = 0;
+ phdr.p_vaddr = mapping.start_address;
+ phdr.p_filesz = 0;
+ phdr.p_flags = PF_R;
+ phdr.p_memsz = mapping.end_address - mapping.start_address;
+ if (!writea(1, &phdr, sizeof(phdr)))
+ return 1;
+ }
+
+ Nhdr nhdr;
+ memset(&nhdr, 0, sizeof(nhdr));
+ nhdr.n_namesz = 5;
+ nhdr.n_descsz = sizeof(prpsinfo);
+ nhdr.n_type = NT_PRPSINFO;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &crashinfo.prps, sizeof(prpsinfo))) {
+ return 1;
+ }
+
+ nhdr.n_descsz = crashinfo.auxv_length;
+ nhdr.n_type = NT_AUXV;
+ if (!writea(1, &nhdr, sizeof(nhdr)) ||
+ !writea(1, "CORE\0\0\0\0", 8) ||
+ !writea(1, &crashinfo.auxv, crashinfo.auxv_length)) {
+ return 1;
+ }
+
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ if (crashinfo.threads[i].tid == crashinfo.crashing_tid) {
+ WriteThread(crashinfo.threads[i], crashinfo.fatal_signal);
+ break;
+ }
+ }
+
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ if (crashinfo.threads[i].tid != crashinfo.crashing_tid)
+ WriteThread(crashinfo.threads[i], 0);
+ }
+
+ if (note_align) {
+ char scratch[note_align];
+ memset(scratch, 0, sizeof(scratch));
+ if (!writea(1, scratch, sizeof(scratch)))
+ return 1;
+ }
+
+ for (unsigned i = 0; i < crashinfo.threads.size(); ++i) {
+ const CrashedProcess::Thread& thread = crashinfo.threads[i];
+ if (!writea(1, thread.stack, thread.stack_length))
+ return 1;
+ }
+
+ munmap(const_cast<void*>(bytes), st.st_size);
+
+ return 0;
+}
diff --git a/breakpad/linux/minidump_file_writer.cc b/breakpad/linux/minidump_file_writer.cc
new file mode 100644
index 0000000..6b02416
--- /dev/null
+++ b/breakpad/linux/minidump_file_writer.cc
@@ -0,0 +1,254 @@
+// Copyright (c) 2006, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// minidump_file_writer.cc: Minidump file writer implementation.
+//
+// See minidump_file_writer.h for documentation.
+
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "breakpad/linux/linux_syscall_support.h"
+#include "breakpad/linux/linux_libc_support.h"
+#include "client/minidump_file_writer-inl.h"
+#include "common/string_conversion.h"
+
+namespace google_breakpad {
+
+const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1);
+
+MinidumpFileWriter::MinidumpFileWriter() : file_(-1), position_(0), size_(0) {
+}
+
+MinidumpFileWriter::~MinidumpFileWriter() {
+ Close();
+}
+
+bool MinidumpFileWriter::Open(const char *path) {
+ assert(file_ == -1);
+ file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
+
+ return file_ != -1;
+}
+
+bool MinidumpFileWriter::Close() {
+ bool result = true;
+
+ if (file_ != -1) {
+ ftruncate(file_, position_);
+ result = (sys_close(file_) == 0);
+ file_ = -1;
+ }
+
+ return result;
+}
+
+bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str,
+ unsigned int length,
+ TypedMDRVA<MDString> *mdstring) {
+ bool result = true;
+ if (sizeof(wchar_t) == sizeof(u_int16_t)) {
+ // Shortcut if wchar_t is the same size as MDString's buffer
+ result = mdstring->Copy(str, mdstring->get()->length);
+ } else {
+ u_int16_t out[2];
+ int out_idx = 0;
+
+ // Copy the string character by character
+ while (length && result) {
+ UTF32ToUTF16Char(*str, out);
+ if (!out[0])
+ return false;
+
+ // Process one character at a time
+ --length;
+ ++str;
+
+ // Append the one or two UTF-16 characters. The first one will be non-
+ // zero, but the second one may be zero, depending on the conversion from
+ // UTF-32.
+ int out_count = out[1] ? 2 : 1;
+ int out_size = sizeof(u_int16_t) * out_count;
+ result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
+ out_idx += out_count;
+ }
+ }
+ return result;
+}
+
+bool MinidumpFileWriter::CopyStringToMDString(const char *str,
+ unsigned int length,
+ TypedMDRVA<MDString> *mdstring) {
+ bool result = true;
+ u_int16_t out[2];
+ int out_idx = 0;
+
+ // Copy the string character by character
+ while (length && result) {
+ int conversion_count = UTF8ToUTF16Char(str, length, out);
+ if (!conversion_count)
+ return false;
+
+ // Move the pointer along based on the nubmer of converted characters
+ length -= conversion_count;
+ str += conversion_count;
+
+ // Append the one or two UTF-16 characters
+ int out_count = out[1] ? 2 : 1;
+ int out_size = sizeof(u_int16_t) * out_count;
+ result = mdstring->CopyIndexAfterObject(out_idx, out, out_size);
+ out_idx += out_count;
+ }
+ return result;
+}
+
+template <typename CharType>
+bool MinidumpFileWriter::WriteStringCore(const CharType *str,
+ unsigned int length,
+ MDLocationDescriptor *location) {
+ assert(str);
+ assert(location);
+ // Calculate the mdstring length by either limiting to |length| as passed in
+ // or by finding the location of the NULL character.
+ unsigned int mdstring_length = 0;
+ if (!length)
+ length = INT_MAX;
+ for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length)
+ ;
+
+ // Allocate the string buffer
+ TypedMDRVA<MDString> mdstring(this);
+ if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(u_int16_t)))
+ return false;
+
+ // Set length excluding the NULL and copy the string
+ mdstring.get()->length = mdstring_length * sizeof(u_int16_t);
+ bool result = CopyStringToMDString(str, mdstring_length, &mdstring);
+
+ // NULL terminate
+ if (result) {
+ u_int16_t ch = 0;
+ result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch));
+
+ if (result)
+ *location = mdstring.location();
+ }
+
+ return result;
+}
+
+bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length,
+ MDLocationDescriptor *location) {
+ return WriteStringCore(str, length, location);
+}
+
+bool MinidumpFileWriter::WriteString(const char *str, unsigned int length,
+ MDLocationDescriptor *location) {
+ return WriteStringCore(str, length, location);
+}
+
+bool MinidumpFileWriter::WriteMemory(const void *src, size_t size,
+ MDMemoryDescriptor *output) {
+ assert(src);
+ assert(output);
+ UntypedMDRVA mem(this);
+
+ if (!mem.Allocate(size))
+ return false;
+ if (!mem.Copy(src, mem.size()))
+ return false;
+
+ output->start_of_memory_range = reinterpret_cast<u_int64_t>(src);
+ output->memory = mem.location();
+
+ return true;
+}
+
+MDRVA MinidumpFileWriter::Allocate(size_t size) {
+ assert(size);
+ assert(file_ != -1);
+ size_t aligned_size = (size + 7) & ~7; // 64-bit alignment
+
+ if (position_ + aligned_size > size_) {
+ size_t growth = aligned_size;
+ size_t minimal_growth = getpagesize();
+
+ // Ensure that the file grows by at least the size of a memory page
+ if (growth < minimal_growth)
+ growth = minimal_growth;
+
+ size_t new_size = size_ + growth;
+ if (ftruncate(file_, new_size) != 0)
+ return kInvalidMDRVA;
+
+ size_ = new_size;
+ }
+
+ MDRVA current_position = position_;
+ position_ += static_cast<MDRVA>(aligned_size);
+
+ return current_position;
+}
+
+bool MinidumpFileWriter::Copy(MDRVA position, const void *src, ssize_t size) {
+ assert(src);
+ assert(size);
+ assert(file_ != -1);
+
+ // Ensure that the data will fit in the allocated space
+ if (size + position > size_)
+ return false;
+
+ // Seek and write the data
+ if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) {
+ if (sys_write(file_, src, size) == size)
+ return true;
+ }
+
+ return false;
+}
+
+bool UntypedMDRVA::Allocate(size_t size) {
+ assert(size_ == 0);
+ size_ = size;
+ position_ = writer_->Allocate(size_);
+ return position_ != MinidumpFileWriter::kInvalidMDRVA;
+}
+
+bool UntypedMDRVA::Copy(MDRVA position, const void *src, size_t size) {
+ assert(src);
+ assert(size);
+ assert(position + size <= position_ + size_);
+ return writer_->Copy(position, src, size);
+}
+
+} // namespace google_breakpad
diff --git a/breakpad/linux/minidump_format_linux.h b/breakpad/linux/minidump_format_linux.h
new file mode 100644
index 0000000..b710462
--- /dev/null
+++ b/breakpad/linux/minidump_format_linux.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_MINIDUMP_FORMAT_LINUX_H_
+#define CLIENT_LINUX_MINIDUMP_FORMAT_LINUX_H_
+
+// These are additional minidump stream values which are specific to the linux
+// breakpad implementation.
+enum {
+ MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */
+ MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */
+ MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */
+ MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */
+ MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */
+ MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */
+};
+
+#endif // CLIENT_LINUX_MINIDUMP_FORMAT_LINUX_H_
diff --git a/breakpad/linux/minidump_writer.cc b/breakpad/linux/minidump_writer.cc
new file mode 100644
index 0000000..5a1d6c6
--- /dev/null
+++ b/breakpad/linux/minidump_writer.cc
@@ -0,0 +1,611 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// This code writes out minidump files:
+// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx
+//
+// Minidumps are a Microsoft format which Breakpad uses for recording crash
+// dumps. This code has to run in a compromised environment (the address space
+// may have received SIGSEGV), thus the following rules apply:
+// * You may not enter the dynamic linker. This means that we cannot call
+// any symbols in a shared library (inc libc). Because of this we replace
+// libc functions in linux_libc_support.h.
+// * You may not call syscalls via the libc wrappers. This rule is a subset
+// of the first rule but it bears repeating. We have direct wrappers
+// around the system calls in linux_syscall_support.h.
+// * You may not malloc. There's an alternative allocator in memory.h and
+// a canonical instance in the LinuxDumper object. We use the placement
+// new form to allocate objects and we don't delete them.
+
+#include "breakpad/linux/minidump_writer.h"
+#include "client/minidump_file_writer-inl.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ucontext.h>
+#include <sys/user.h>
+
+#include "client/minidump_file_writer.h"
+#include "google_breakpad/common/minidump_format.h"
+#include "google_breakpad/common/minidump_cpu_amd64.h"
+#include "google_breakpad/common/minidump_cpu_x86.h"
+
+#include "breakpad/linux/exception_handler.h"
+#include "breakpad/linux/linux_dumper.h"
+#include "breakpad/linux/linux_libc_support.h"
+#include "breakpad/linux/linux_syscall_support.h"
+#include "breakpad/linux/minidump_format_linux.h"
+
+// Minidump defines register structures which are different from the raw
+// structures which we get from the kernel. These are platform specific
+// functions to juggle the ucontext and user structures into minidump format.
+#if defined(__i386)
+typedef MDRawContextX86 RawContextCPU;
+
+// Write a uint16_t to memory
+// out: memory location to write to
+// v: value to write.
+static void U16(void* out, uint16_t v) {
+ memcpy(out, &v, sizeof(v));
+}
+
+// Write a uint32_t to memory
+// out: memory location to write to
+// v: value to write.
+static void U32(void* out, uint32_t v) {
+ memcpy(out, &v, sizeof(v));
+}
+
+// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format
+// out: the minidump structure
+// info: the collection of register structures.
+static void CPUFillFromThreadInfo(MDRawContextX86 *out,
+ const google_breakpad::ThreadInfo &info) {
+ out->context_flags = MD_CONTEXT_X86_ALL;
+
+ out->dr0 = info.dregs[0];
+ out->dr1 = info.dregs[1];
+ out->dr2 = info.dregs[2];
+ out->dr3 = info.dregs[3];
+ // 4 and 5 deliberatly omitted because they aren't included in the minidump
+ // format.
+ out->dr6 = info.dregs[6];
+ out->dr7 = info.dregs[7];
+
+ out->gs = info.regs.xgs;
+ out->fs = info.regs.xfs;
+ out->es = info.regs.xes;
+ out->ds = info.regs.xds;
+
+ out->edi = info.regs.edi;
+ out->esi = info.regs.esi;
+ out->ebx = info.regs.ebx;
+ out->edx = info.regs.edx;
+ out->ecx = info.regs.ecx;
+ out->eax = info.regs.eax;
+
+ out->ebp = info.regs.ebp;
+ out->eip = info.regs.eip;
+ out->cs = info.regs.xcs;
+ out->eflags = info.regs.eflags;
+ out->esp = info.regs.esp;
+ out->ss = info.regs.xss;
+
+ out->float_save.control_word = info.fpregs.cwd;
+ out->float_save.status_word = info.fpregs.swd;
+ out->float_save.tag_word = info.fpregs.twd;
+ out->float_save.error_offset = info.fpregs.fip;
+ out->float_save.error_selector = info.fpregs.fcs;
+ out->float_save.data_offset = info.fpregs.foo;
+ out->float_save.data_selector = info.fpregs.fos;
+
+ // 8 registers * 10 bytes per register.
+ memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8);
+
+ // This matches the Intel fpsave format.
+ U16(out->extended_registers + 0, info.fpregs.cwd);
+ U16(out->extended_registers + 2, info.fpregs.swd);
+ U16(out->extended_registers + 4, info.fpregs.twd);
+ U16(out->extended_registers + 6, info.fpxregs.fop);
+ U32(out->extended_registers + 8, info.fpxregs.fip);
+ U16(out->extended_registers + 12, info.fpxregs.fcs);
+ U32(out->extended_registers + 16, info.fpregs.foo);
+ U16(out->extended_registers + 20, info.fpregs.fos);
+ U32(out->extended_registers + 24, info.fpxregs.mxcsr);
+
+ memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128);
+ memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128);
+}
+
+// Juggle an x86 ucontext into minidump format
+// out: the minidump structure
+// info: the collection of register structures.
+static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc) {
+ const greg_t* regs = uc->uc_mcontext.gregs;
+ const fpregset_t fp = uc->uc_mcontext.fpregs;
+
+ out->context_flags = MD_CONTEXT_X86_FULL |
+ MD_CONTEXT_X86_FLOATING_POINT;
+
+ out->gs = regs[REG_GS];
+ out->fs = regs[REG_FS];
+ out->es = regs[REG_ES];
+ out->ds = regs[REG_DS];
+
+ out->edi = regs[REG_EDI];
+ out->esi = regs[REG_ESI];
+ out->ebx = regs[REG_EBX];
+ out->edx = regs[REG_EDX];
+ out->ecx = regs[REG_ECX];
+ out->eax = regs[REG_EAX];
+
+ out->ebp = regs[REG_EBP];
+ out->eip = regs[REG_EIP];
+ out->cs = regs[REG_CS];
+ out->eflags = regs[REG_EFL];
+ out->esp = regs[REG_UESP];
+ out->ss = regs[REG_SS];
+
+ out->float_save.control_word = fp->cw;
+ out->float_save.status_word = fp->sw;
+ out->float_save.tag_word = fp->tag;
+ out->float_save.error_offset = fp->ipoff;
+ out->float_save.error_selector = fp->cssel;
+ out->float_save.data_offset = fp->dataoff;
+ out->float_save.data_selector = fp->datasel;
+
+ // 8 registers * 10 bytes per register.
+ memcpy(out->float_save.register_area, fp->_st, 10 * 8);
+}
+
+#elif defined(__x86_64)
+typedef MDRawContextAMD64 RawContextCPU;
+
+static void CPUFillFromThreadInfo(MDRawContextAMD64 *out,
+ const google_breakpad::ThreadInfo &info) {
+ out->context_flags = MD_CONTEXT_AMD64_FULL |
+ MD_CONTEXT_AMD64_SEGMENTS;
+
+ out->cs = info.regs.cs;
+
+ out->ds = info.regs.ds;
+ out->es = info.regs.es;
+ out->fs = info.regs.fs;
+ out->gs = info.regs.gs;
+
+ out->ss = info.regs.ss;
+ out->eflags = info.regs.eflags;
+
+ out->dr0 = info.dregs[0];
+ out->dr1 = info.dregs[1];
+ out->dr2 = info.dregs[2];
+ out->dr3 = info.dregs[3];
+ // 4 and 5 deliberatly omitted because they aren't included in the minidump
+ // format.
+ out->dr6 = info.dregs[6];
+ out->dr7 = info.dregs[7];
+
+ out->rax = info.regs.rax;
+ out->rcx = info.regs.rcx;
+ out->rdx = info.regs.rdx;
+ out->rbx = info.regs.rbx;
+
+ out->rsp = info.regs.rsp;
+
+ out->rbp = info.regs.rbp;
+ out->rsi = info.regs.rsi;
+ out->rdi = info.regs.rdi;
+ out->r8 = info.regs.r8;
+ out->r9 = info.regs.r9;
+ out->r10 = info.regs.r10;
+ out->r11 = info.regs.r11;
+ out->r12 = info.regs.r12;
+ out->r13 = info.regs.r13;
+ out->r14 = info.regs.r14;
+ out->r15 = info.regs.r15;
+
+ out->rip = info.regs.rip;
+
+ out->flt_save.control_word = info.fpregs.cwd;
+ out->flt_save.status_word = info.fpregs.swd;
+ out->flt_save.tag_word = info.fpregs.twd;
+ out->flt_save.error_opcode = info.fpregs.fop;
+ out->flt_save.error_offset = info.fpregs.rip;
+ out->flt_save.error_selector = 0; // We don't have this.
+ out->flt_save.data_offset = info.fpregs.rdp;
+ out->flt_save.data_selector = 0; // We don't have this.
+ out->flt_save.mx_csr = info.fpregs.mxcsr;
+ out->flt_save.mx_csr_mask = info.fpregs.mxcsr_mask;
+ memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16);
+ memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16);
+}
+
+static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc) {
+ const greg_t* regs = uc->gregs;
+ const fpregset_t fpregs = uc->fpregs;
+
+ out->context_flags = MD_CONTEXT_AMD64_FULL;
+
+ out->cs = regs[REG_CSGSFS] & 0xffff;
+
+ out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff;
+ out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff;
+
+ out->eflags = regs[REG_EFL];
+
+ out->rax = regs[REG_RAX];
+ out->rcx = regs[REG_RCX];
+ out->rdx = regs[REG_RDX];
+ out->rbx = regs[REG_RBX];
+
+ out->rsp = regs[REG_RSP];
+ out->rbp = regs[REG_RBP];
+ out->rsi = regs[REG_RSI];
+ out->rdi = regs[REG_RDI];
+ out->r8 = regs[REG_R8];
+ out->r9 = regs[REG_R9];
+ out->r10 = regs[REG_R10];
+ out->r11 = regs[REG_R11];
+ out->r12 = regs[REG_R12];
+ out->r13 = regs[REG_R13];
+ out->r14 = regs[REG_R14];
+ out->r15 = regs[REG_R15];
+
+ out->rip = regs[REG_RIP];
+
+ out->flt_save.control_word = fpregs->cwd;
+ out->flt_save.status_word = fpregs->swd;
+ out->flt_save.tag_word = fpregs->ftw;
+ out->flt_save.error_opcode = fpregs->fop;
+ out->flt_save.error_offset = fpregs->rip;
+ out->flt_save.data_offset = fpregs->rdp;
+ out->flt_save.error_selector = 0; // We don't have this.
+ out->flt_save.data_selector = 0; // We don't have this.
+ out->flt_save.mx_csr = fpregs->mxcsr;
+ out->flt_save.mx_csr_mask = fpregs->mxcsr_mask;
+ memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16);
+ memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16);
+}
+
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
+namespace google_breakpad {
+
+class MinidumpWriter {
+ public:
+ MinidumpWriter(const char* filename,
+ pid_t crashing_pid,
+ const ExceptionHandler::CrashContext* context)
+ : filename_(filename),
+ siginfo_(&context->siginfo),
+ ucontext_(&context->context),
+ crashing_tid_(context->tid),
+ dumper_(crashing_pid) {
+ }
+
+ bool Init() {
+ return dumper_.Init() && minidump_writer_.Open(filename_) &&
+ dumper_.ThreadsSuspend();
+ }
+
+ ~MinidumpWriter() {
+ minidump_writer_.Close();
+ dumper_.ThreadsResume();
+ }
+
+ bool Dump() {
+ // A minidump file contains a number of tagged streams. This is the number
+ // of stream which we write.
+ static const unsigned kNumWriters = 10;
+
+ TypedMDRVA<MDRawHeader> header(&minidump_writer_);
+ TypedMDRVA<MDRawDirectory> dir(&minidump_writer_);
+ if (!header.Allocate())
+ return false;
+ if (!dir.AllocateArray(kNumWriters))
+ return false;
+ memset(header.get(), 0, sizeof(MDRawHeader));
+
+ header.get()->signature = MD_HEADER_SIGNATURE;
+ header.get()->version = MD_HEADER_VERSION;
+ header.get()->time_date_stamp = time(NULL);
+ header.get()->stream_count = kNumWriters;
+ header.get()->stream_directory_rva = dir.position();
+
+ unsigned dir_index = 0;
+ MDRawDirectory dirent;
+
+ if (!WriteThreadListStream(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ if (!WriteMappings(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ if (!WriteExceptionStream(&dirent))
+ return false;
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_CPU_INFO;
+ if (!WriteFile(&dirent.location, "/proc/cpuinfo"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_PROC_STATUS;
+ if (!WriteProcFile(&dirent.location, crashing_tid_, "status"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_LSB_RELEASE;
+ if (!WriteFile(&dirent.location, "/etc/lsb-release"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_CMD_LINE;
+ if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_ENVIRON;
+ if (!WriteProcFile(&dirent.location, crashing_tid_, "environ"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_AUXV;
+ if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ dirent.stream_type = MD_LINUX_AUXV;
+ if (!WriteProcFile(&dirent.location, crashing_tid_, "maps"))
+ NullifyDirectoryEntry(&dirent);
+ dir.CopyIndex(dir_index++, &dirent);
+
+ // If you add more directory entries, don't forget to update kNumWriters,
+ // above.
+
+ dumper_.ThreadsResume();
+ return true;
+ }
+
+ // Write information about the threads.
+ bool WriteThreadListStream(MDRawDirectory* dirent) {
+ const unsigned num_threads = dumper_.threads().size();
+
+ TypedMDRVA<uint32_t> list(&minidump_writer_);
+ if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread)))
+ return false;
+
+ dirent->stream_type = MD_THREAD_LIST_STREAM;
+ dirent->location = list.location();
+
+ *list.get() = num_threads;
+
+ for (unsigned i = 0; i < num_threads; ++i) {
+ MDRawThread thread;
+ my_memset(&thread, 0, sizeof(thread));
+ thread.thread_id = dumper_.threads()[i];
+ // We have a different source of information for the crashing thread. If
+ // we used the actual state of the thread we would find it running in the
+ // signal handler with the alternative stack, which would be deeply
+ // unhelpful.
+ if (thread.thread_id == crashing_tid_) {
+ const void* stack;
+ size_t stack_len;
+ if (!dumper_.GetStackInfo(&stack, &stack_len, GetStackPointer()))
+ return false;
+ UntypedMDRVA memory(&minidump_writer_);
+ if (!memory.Allocate(stack_len))
+ return false;
+ uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len);
+ dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len);
+ memory.Copy(stack_copy, stack_len);
+ thread.stack.start_of_memory_range = (uintptr_t) (stack);
+ thread.stack.memory = memory.location();
+ TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
+ if (!cpu.Allocate())
+ return false;
+ my_memset(cpu.get(), 0, sizeof(RawContextCPU));
+ CPUFillFromUContext(cpu.get(), ucontext_);
+ thread.thread_context = cpu.location();
+ crashing_thread_context_ = cpu.location();
+ } else {
+ ThreadInfo info;
+ if (!dumper_.ThreadInfoGet(dumper_.threads()[i], &info))
+ return false;
+ UntypedMDRVA memory(&minidump_writer_);
+ if (!memory.Allocate(info.stack_len))
+ return false;
+ uint8_t* stack_copy =
+ (uint8_t*) dumper_.allocator()->Alloc(info.stack_len);
+ dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack,
+ info.stack_len);
+ memory.Copy(stack_copy, info.stack_len);
+ thread.stack.start_of_memory_range = (uintptr_t)(info.stack);
+ thread.stack.memory = memory.location();
+ TypedMDRVA<RawContextCPU> cpu(&minidump_writer_);
+ if (!cpu.Allocate())
+ return false;
+ my_memset(cpu.get(), 0, sizeof(RawContextCPU));
+ CPUFillFromThreadInfo(cpu.get(), info);
+ thread.thread_context = cpu.location();
+ }
+
+ list.CopyIndexAfterObject(i, &thread, sizeof(thread));
+ }
+
+ return true;
+ }
+
+ // Write information about the mappings in effect. Because we are using the
+ // minidump format, the information about the mappings is pretty limited.
+ // Because of this, we also include the full, unparsed, /proc/$x/maps file in
+ // another stream in the file.
+ bool WriteMappings(MDRawDirectory* dirent) {
+ const unsigned num_mappings = dumper_.mappings().size();
+
+ TypedMDRVA<uint32_t> list(&minidump_writer_);
+ if (!list.AllocateObjectAndArray(num_mappings, sizeof(MDRawModule)))
+ return false;
+
+ dirent->stream_type = MD_MODULE_LIST_STREAM;
+ dirent->location = list.location();
+ *list.get() = num_mappings;
+
+ for (unsigned i = 0; i < num_mappings; ++i) {
+ const MappingInfo& mapping = *dumper_.mappings()[i];
+ MDRawModule mod;
+ my_memset(&mod, 0, sizeof(mod));
+ mod.base_of_image = mapping.start_addr;
+ mod.size_of_image = mapping.size;
+ UntypedMDRVA memory(&minidump_writer_);
+ const size_t filename_len = my_strlen(mapping.name);
+
+ if (filename_len) {
+ MDLocationDescriptor ld;
+ if (!minidump_writer_.WriteString(mapping.name, filename_len, &ld))
+ return false;
+ mod.module_name_rva = ld.rva;
+ }
+
+ list.CopyIndexAfterObject(i, &mod, sizeof(mod));
+ }
+
+ return true;
+ }
+
+ bool WriteExceptionStream(MDRawDirectory* dirent) {
+ TypedMDRVA<MDRawExceptionStream> exc(&minidump_writer_);
+ if (!exc.Allocate())
+ return false;
+ my_memset(exc.get(), 0, sizeof(MDRawExceptionStream));
+
+ dirent->stream_type = MD_EXCEPTION_STREAM;
+ dirent->location = exc.location();
+
+ exc.get()->thread_id = crashing_tid_;
+ exc.get()->exception_record.exception_code = siginfo_->si_signo;
+ exc.get()->exception_record.exception_address =
+ (uintptr_t) siginfo_->si_addr;
+ exc.get()->thread_context = crashing_thread_context_;
+
+ return true;
+ }
+
+ private:
+#if defined(__i386)
+ uintptr_t GetStackPointer() {
+ return ucontext_->uc_mcontext.gregs[REG_ESP];
+ }
+#elif defined(__x86_64)
+ uintptr_t GetStackPointer() {
+ return ucontext_->uc_mcontext.gregs[REG_RSP];
+ }
+#else
+#error "This code has not been ported to your platform yet."
+#endif
+
+ void NullifyDirectoryEntry(MDRawDirectory* dirent) {
+ dirent->stream_type = 0;
+ dirent->location.data_size = 0;
+ dirent->location.rva = 0;
+ }
+
+ bool WriteFile(MDLocationDescriptor* result, const char* filename) {
+ const int fd = sys_open(filename, O_RDONLY, 0);
+ if (fd < 0)
+ return false;
+
+ // We can't stat the files because several of the files that we want to
+ // read are kernel seqfiles, which always have a length of zero. So we have
+ // to read as much as we can into a buffer.
+ static const unsigned kMaxFileSize = 1024;
+ uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize);
+
+ size_t done = 0;
+ while (done < kMaxFileSize) {
+ ssize_t r;
+ do {
+ r = sys_read(fd, data + done, kMaxFileSize - done);
+ } while (r == -1 && errno == EINTR);
+
+ if (r < 1)
+ break;
+ done += r;
+ }
+ sys_close(fd);
+
+ if (!done)
+ return false;
+
+ UntypedMDRVA memory(&minidump_writer_);
+ if (!memory.Allocate(done))
+ return false;
+ memory.Copy(data, done);
+ *result = memory.location();
+ return true;
+ }
+
+ bool WriteProcFile(MDLocationDescriptor* result, pid_t pid,
+ const char* filename) {
+ char buf[80];
+ memcpy(buf, "/proc/", 6);
+ const unsigned pid_len = my_int_len(pid);
+ my_itos(buf + 6, pid, pid_len);
+ buf[6 + pid_len] = '/';
+ memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1);
+ return WriteFile(result, buf);
+ }
+
+ const char* const filename_; // output filename
+ const siginfo_t* const siginfo_; // from the signal handler (see sigaction)
+ const struct ucontext* const ucontext_; // also from the signal handler
+ const pid_t crashing_tid_; // the process which actually crashed
+ LinuxDumper dumper_;
+ MinidumpFileWriter minidump_writer_;
+ MDLocationDescriptor crashing_thread_context_;
+};
+
+bool WriteMinidump(const char* filename, pid_t crashing_process,
+ const void* blob, size_t blob_size) {
+ if (blob_size != sizeof(ExceptionHandler::CrashContext))
+ return false;
+ const ExceptionHandler::CrashContext* context =
+ reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
+ MinidumpWriter writer(filename, crashing_process, context);
+ if (!writer.Init())
+ return false;
+ return writer.Dump();
+}
+
+} // namespace google_breakpad
diff --git a/breakpad/linux/minidump_writer.h b/breakpad/linux/minidump_writer.h
new file mode 100644
index 0000000..b2303f7
--- /dev/null
+++ b/breakpad/linux/minidump_writer.h
@@ -0,0 +1,53 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_WRITER_H_
+#define CLIENT_LINUX_HANDLER_MINIDUMP_WRITER_H_
+
+#include <stdint.h>
+#include <unistd.h>
+
+namespace google_breakpad {
+
+// Write a minidump to the filesystem. This function does not malloc nor use
+// libc functions which may. Thus, it can be used in contexts where the state
+// of the heap may be corrupt.
+// filename: the filename to write to. This is opened O_EXCL and fails if
+// open fails.
+// crashing_process: the pid of the crashing process. This must be trusted.
+// blob: a blob of data from the crashing process. See exception_handler.h
+// blob_size: the length of |blob|, in bytes
+//
+// Returns true iff successful.
+bool WriteMinidump(const char* filename, pid_t crashing_process,
+ const void* blob, size_t blob_size);
+
+} // namespace google_breakpad
+
+#endif // CLIENT_LINUX_HANDLER_MINIDUMP_WRITER_H_
diff --git a/breakpad/linux/minidump_writer_unittest.cc b/breakpad/linux/minidump_writer_unittest.cc
new file mode 100644
index 0000000..1f0eac3
--- /dev/null
+++ b/breakpad/linux/minidump_writer_unittest.cc
@@ -0,0 +1,70 @@
+// Copyright (c) 2009, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "base/eintr_wrapper.h"
+#include "breakpad/linux/exception_handler.h"
+#include "breakpad/linux/minidump_writer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using namespace google_breakpad;
+
+namespace {
+typedef testing::Test MinidumpWriterTest;
+}
+
+TEST(MinidumpWriterTest, Setup) {
+ int fds[2];
+ ASSERT_NE(-1, pipe(fds));
+
+ const pid_t child = fork();
+ if (child == 0) {
+ close(fds[1]);
+ char b;
+ HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
+ close(fds[0]);
+ syscall(__NR_exit);
+ }
+ close(fds[0]);
+
+ ExceptionHandler::CrashContext context;
+ memset(&context, 0, sizeof(context));
+
+ char templ[] = "/tmp/minidump-writer-unittest-XXXXXX";
+ mktemp(templ);
+ ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context)));
+ struct stat st;
+ ASSERT_EQ(stat(templ, &st), 0);
+ ASSERT_GT(st.st_size, 0u);
+ unlink(templ);
+
+ close(fds[1]);
+}