init
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								sdk/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sdk/.DS_Store
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										53
									
								
								sdk/breakpad/client/linux/crash_generation/client_info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								sdk/breakpad/client/linux/crash_generation/client_info.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ | ||||
| #define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class CrashGenerationServer; | ||||
|  | ||||
| class ClientInfo { | ||||
|  public: | ||||
|   ClientInfo(pid_t pid, CrashGenerationServer* crash_server) | ||||
|     : crash_server_(crash_server), | ||||
|       pid_(pid) {} | ||||
|  | ||||
|   CrashGenerationServer* crash_server() const { return crash_server_; } | ||||
|   pid_t pid() const { return pid_; } | ||||
|  | ||||
|  private: | ||||
|   CrashGenerationServer* crash_server_; | ||||
|   pid_t pid_; | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ | ||||
| @@ -0,0 +1,92 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <stdio.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <algorithm> | ||||
|  | ||||
| #include "client/linux/crash_generation/crash_generation_client.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/linux/ignore_ret.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| bool | ||||
| CrashGenerationClient::RequestDump(const void* blob, size_t blob_size) | ||||
| { | ||||
|   int fds[2]; | ||||
|   sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds); | ||||
|   static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); | ||||
|  | ||||
|   struct kernel_msghdr msg; | ||||
|   my_memset(&msg, 0, sizeof(struct kernel_msghdr)); | ||||
|   struct kernel_iovec iov[1]; | ||||
|   iov[0].iov_base = const_cast<void*>(blob); | ||||
|   iov[0].iov_len = blob_size; | ||||
|  | ||||
|   msg.msg_iov = iov; | ||||
|   msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); | ||||
|   char cmsg[kControlMsgSize]; | ||||
|   my_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* p = reinterpret_cast<int*>(CMSG_DATA(hdr)); | ||||
|   *p = fds[1]; | ||||
|  | ||||
|   ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); | ||||
|   sys_close(fds[1]); | ||||
|   if (ret <= 0) | ||||
|     return false; | ||||
|  | ||||
|   // wait for an ACK from the server | ||||
|   char b; | ||||
|   IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| //static | ||||
| CrashGenerationClient* | ||||
| CrashGenerationClient::TryCreate(int server_fd) | ||||
| { | ||||
|   if (0 > server_fd) | ||||
|     return NULL; | ||||
|   return new CrashGenerationClient(server_fd); | ||||
| } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,69 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ | ||||
| #define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ | ||||
|  | ||||
| #include <stddef.h> | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class CrashGenerationClient { | ||||
| public: | ||||
|   ~CrashGenerationClient() | ||||
|   { | ||||
|   } | ||||
|  | ||||
|   // Request the crash server to generate a dump.  |blob| is a hack, | ||||
|   // see exception_handler.h and minidump_writer.h | ||||
|   // | ||||
|   // Return true if the dump was successful; false otherwise. | ||||
|   bool RequestDump(const void* blob, size_t blob_size); | ||||
|  | ||||
|   // Return a new CrashGenerationClient if |server_fd| is valid and | ||||
|   // connects to a CrashGenerationServer.  Otherwise, return NULL. | ||||
|   // The returned CrashGenerationClient* is owned by the caller of | ||||
|   // this function. | ||||
|   static CrashGenerationClient* TryCreate(int server_fd); | ||||
|  | ||||
| private: | ||||
|   CrashGenerationClient(int server_fd) : server_fd_(server_fd) | ||||
|   { | ||||
|   } | ||||
|  | ||||
|   int server_fd_; | ||||
|  | ||||
|   // prevent copy construction and assignment | ||||
|   CrashGenerationClient(const CrashGenerationClient&); | ||||
|   CrashGenerationClient& operator=(const CrashGenerationClient&); | ||||
| }; | ||||
|  | ||||
| } // namespace google_breakpad | ||||
|  | ||||
| #endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ | ||||
| @@ -0,0 +1,464 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <dirent.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
| #include <poll.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <sys/socket.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| #include "client/linux/crash_generation/crash_generation_server.h" | ||||
| #include "client/linux/crash_generation/client_info.h" | ||||
| #include "client/linux/handler/exception_handler.h" | ||||
| #include "client/linux/minidump_writer/minidump_writer.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/linux/guid_creator.h" | ||||
| #include "common/linux/safe_readlink.h" | ||||
|  | ||||
| static const char kCommandQuit = 'x'; | ||||
|  | ||||
| static bool | ||||
| GetInodeForFileDescriptor(ino_t* inode_out, int fd) | ||||
| { | ||||
|   assert(inode_out); | ||||
|  | ||||
|   struct stat buf; | ||||
|   if (fstat(fd, &buf) < 0) | ||||
|     return false; | ||||
|  | ||||
|   if (!S_ISSOCK(buf.st_mode)) | ||||
|     return false; | ||||
|  | ||||
|   *inode_out = buf.st_ino; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // expected prefix of the target of the /proc/self/fd/%d link for a socket | ||||
| static const char kSocketLinkPrefix[] = "socket:["; | ||||
|  | ||||
| // Parse a symlink in /proc/pid/fd/$x and return the inode number of the | ||||
| // socket. | ||||
| //   inode_out: (output) set to the inode number on success | ||||
| //   path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) | ||||
| static bool | ||||
| GetInodeForProcPath(ino_t* inode_out, const char* path) | ||||
| { | ||||
|   assert(inode_out); | ||||
|   assert(path); | ||||
|  | ||||
|   char buf[PATH_MAX]; | ||||
|   if (!google_breakpad::SafeReadLink(path, buf)) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   char* endptr; | ||||
|   const uint64_t inode_ul = | ||||
|       strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); | ||||
|   if (*endptr != ']') | ||||
|     return false; | ||||
|  | ||||
|   if (inode_ul == ULLONG_MAX) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   *inode_out = inode_ul; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) | ||||
| { | ||||
|   assert(pid_out); | ||||
|   bool already_found = false; | ||||
|  | ||||
|   DIR* proc = opendir("/proc"); | ||||
|   if (!proc) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   std::vector<pid_t> pids; | ||||
|  | ||||
|   struct dirent* dent; | ||||
|   while ((dent = readdir(proc))) { | ||||
|     char* endptr; | ||||
|     const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); | ||||
|     if (pid_ul == ULONG_MAX || '\0' != *endptr) | ||||
|       continue; | ||||
|     pids.push_back(pid_ul); | ||||
|   } | ||||
|   closedir(proc); | ||||
|  | ||||
|   for (std::vector<pid_t>::const_iterator | ||||
|        i = pids.begin(); i != pids.end(); ++i) { | ||||
|     const pid_t current_pid = *i; | ||||
|     char buf[PATH_MAX]; | ||||
|     snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); | ||||
|     DIR* fd = opendir(buf); | ||||
|     if (!fd) | ||||
|       continue; | ||||
|  | ||||
|     while ((dent = readdir(fd))) { | ||||
|       if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, | ||||
|                    dent->d_name) >= static_cast<int>(sizeof(buf))) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       ino_t fd_inode; | ||||
|       if (GetInodeForProcPath(&fd_inode, buf) | ||||
|           && fd_inode == socket_inode) { | ||||
|         if (already_found) { | ||||
|           closedir(fd); | ||||
|           return false; | ||||
|         } | ||||
|  | ||||
|         already_found = true; | ||||
|         *pid_out = current_pid; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     closedir(fd); | ||||
|   } | ||||
|  | ||||
|   return already_found; | ||||
| } | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| CrashGenerationServer::CrashGenerationServer( | ||||
|   const int listen_fd, | ||||
|   OnClientDumpRequestCallback dump_callback, | ||||
|   void* dump_context, | ||||
|   OnClientExitingCallback exit_callback, | ||||
|   void* exit_context, | ||||
|   bool generate_dumps, | ||||
|   const string* dump_path) : | ||||
|     server_fd_(listen_fd), | ||||
|     dump_callback_(dump_callback), | ||||
|     dump_context_(dump_context), | ||||
|     exit_callback_(exit_callback), | ||||
|     exit_context_(exit_context), | ||||
|     generate_dumps_(generate_dumps), | ||||
|     started_(false) | ||||
| { | ||||
|   if (dump_path) | ||||
|     dump_dir_ = *dump_path; | ||||
|   else | ||||
|     dump_dir_ = "/tmp"; | ||||
| } | ||||
|  | ||||
| CrashGenerationServer::~CrashGenerationServer() | ||||
| { | ||||
|   if (started_) | ||||
|     Stop(); | ||||
| } | ||||
|  | ||||
| bool | ||||
| CrashGenerationServer::Start() | ||||
| { | ||||
|   if (started_ || 0 > server_fd_) | ||||
|     return false; | ||||
|  | ||||
|   int control_pipe[2]; | ||||
|   if (pipe(control_pipe)) | ||||
|     return false; | ||||
|  | ||||
|   if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) | ||||
|     return false; | ||||
|   if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) | ||||
|     return false; | ||||
|  | ||||
|   if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) | ||||
|     return false; | ||||
|  | ||||
|   control_pipe_in_ = control_pipe[0]; | ||||
|   control_pipe_out_ = control_pipe[1]; | ||||
|  | ||||
|   if (pthread_create(&thread_, NULL, | ||||
|                      ThreadMain, reinterpret_cast<void*>(this))) | ||||
|     return false; | ||||
|  | ||||
|   started_ = true; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void | ||||
| CrashGenerationServer::Stop() | ||||
| { | ||||
|   assert(pthread_self() != thread_); | ||||
|  | ||||
|   if (!started_) | ||||
|     return; | ||||
|  | ||||
|   HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); | ||||
|  | ||||
|   void* dummy; | ||||
|   pthread_join(thread_, &dummy); | ||||
|  | ||||
|   started_ = false; | ||||
| } | ||||
|  | ||||
| //static | ||||
| bool | ||||
| CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) | ||||
| { | ||||
|   int fds[2]; | ||||
|  | ||||
|   if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) | ||||
|     return false; | ||||
|  | ||||
|   static const int on = 1; | ||||
|   // Enable passcred on the server end of the socket | ||||
|   if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) | ||||
|     return false; | ||||
|  | ||||
|   if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) | ||||
|     return false; | ||||
|   if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) | ||||
|     return false; | ||||
|  | ||||
|   *client_fd = fds[0]; | ||||
|   *server_fd = fds[1]; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // The following methods/functions execute on the server thread | ||||
|  | ||||
| void | ||||
| CrashGenerationServer::Run() | ||||
| { | ||||
|   struct pollfd pollfds[2]; | ||||
|   memset(&pollfds, 0, sizeof(pollfds)); | ||||
|  | ||||
|   pollfds[0].fd = server_fd_; | ||||
|   pollfds[0].events = POLLIN; | ||||
|  | ||||
|   pollfds[1].fd = control_pipe_in_; | ||||
|   pollfds[1].events = POLLIN; | ||||
|  | ||||
|   while (true) { | ||||
|     // infinite timeout | ||||
|     int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); | ||||
|     if (-1 == nevents) { | ||||
|       if (EINTR == errno) { | ||||
|         continue; | ||||
|       } else { | ||||
|         return; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) | ||||
|       return; | ||||
|  | ||||
|     if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) | ||||
|       return; | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool | ||||
| CrashGenerationServer::ClientEvent(short revents) | ||||
| { | ||||
|   if (POLLHUP & revents) | ||||
|     return false; | ||||
|   assert(POLLIN & revents); | ||||
|  | ||||
|   // A process has crashed and has signaled us by writing a datagram | ||||
|   // to the death signal socket. The datagram contains the crash context needed | ||||
|   // for writing the minidump as well as a file descriptor and a credentials | ||||
|   // block so that they can't lie about their pid. | ||||
|  | ||||
|   // The length of the control message: | ||||
|   static const unsigned kControlMsgSize = | ||||
|       CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); | ||||
|   // The length of the regular payload: | ||||
|   static const unsigned kCrashContextSize = | ||||
|       sizeof(google_breakpad::ExceptionHandler::CrashContext); | ||||
|  | ||||
|   struct msghdr msg = {0}; | ||||
|   struct iovec iov[1]; | ||||
|   char crash_context[kCrashContextSize]; | ||||
|   char control[kControlMsgSize]; | ||||
|   const ssize_t expected_msg_size = sizeof(crash_context); | ||||
|  | ||||
|   iov[0].iov_base = crash_context; | ||||
|   iov[0].iov_len = sizeof(crash_context); | ||||
|   msg.msg_iov = iov; | ||||
|   msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); | ||||
|   msg.msg_control = control; | ||||
|   msg.msg_controllen = kControlMsgSize; | ||||
|  | ||||
|   const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); | ||||
|   if (msg_size != expected_msg_size) | ||||
|     return true; | ||||
|  | ||||
|   if (msg.msg_controllen != kControlMsgSize || | ||||
|       msg.msg_flags & ~MSG_TRUNC) | ||||
|     return true; | ||||
|  | ||||
|   // Walk the control payload and extract the file descriptor and validated pid. | ||||
|   pid_t crashing_pid = -1; | ||||
|   int signal_fd = -1; | ||||
|   for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; | ||||
|        hdr = CMSG_NXTHDR(&msg, hdr)) { | ||||
|     if (hdr->cmsg_level != SOL_SOCKET) | ||||
|       continue; | ||||
|     if (hdr->cmsg_type == SCM_RIGHTS) { | ||||
|       const unsigned len = hdr->cmsg_len - | ||||
|           (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); | ||||
|       assert(len % sizeof(int) == 0u); | ||||
|       const unsigned num_fds = len / sizeof(int); | ||||
|       if (num_fds > 1 || num_fds == 0) { | ||||
|         // A nasty process could try and send us too many descriptors and | ||||
|         // force a leak. | ||||
|         for (unsigned i = 0; i < num_fds; ++i) | ||||
|           close(reinterpret_cast<int*>(CMSG_DATA(hdr))[i]); | ||||
|         return true; | ||||
|       } else { | ||||
|         signal_fd = reinterpret_cast<int*>(CMSG_DATA(hdr))[0]; | ||||
|       } | ||||
|     } else if (hdr->cmsg_type == SCM_CREDENTIALS) { | ||||
|       const struct ucred *cred = | ||||
|           reinterpret_cast<struct ucred*>(CMSG_DATA(hdr)); | ||||
|       crashing_pid = cred->pid; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (crashing_pid == -1 || signal_fd == -1) { | ||||
|     if (signal_fd) | ||||
|       close(signal_fd); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   // Kernel bug workaround (broken in 2.6.30 at least): | ||||
|   // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID | ||||
|   // namespaces. Thus |crashing_pid| might be garbage from our point of view. | ||||
|   // In the future we can remove this workaround, but we have to wait a couple | ||||
|   // of years to be sure that it's worked its way out into the world. | ||||
|  | ||||
|   ino_t inode_number; | ||||
|   if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) { | ||||
|     close(signal_fd); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { | ||||
|     close(signal_fd); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   string minidump_filename; | ||||
|   if (!MakeMinidumpFilename(minidump_filename)) | ||||
|     return true; | ||||
|  | ||||
|   if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), | ||||
|                                       crashing_pid, crash_context, | ||||
|                                       kCrashContextSize)) { | ||||
|     close(signal_fd); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   if (dump_callback_) { | ||||
|     ClientInfo info(crashing_pid, this); | ||||
|  | ||||
|     dump_callback_(dump_context_, &info, &minidump_filename); | ||||
|   } | ||||
|  | ||||
|   // Send the done signal to the process: it can exit now. | ||||
|   memset(&msg, 0, sizeof(msg)); | ||||
|   struct iovec done_iov; | ||||
|   done_iov.iov_base = const_cast<char*>("\x42"); | ||||
|   done_iov.iov_len = 1; | ||||
|   msg.msg_iov = &done_iov; | ||||
|   msg.msg_iovlen = 1; | ||||
|  | ||||
|   HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); | ||||
|   close(signal_fd); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool | ||||
| CrashGenerationServer::ControlEvent(short revents) | ||||
| { | ||||
|   if (POLLHUP & revents) | ||||
|     return false; | ||||
|   assert(POLLIN & revents); | ||||
|  | ||||
|   char command; | ||||
|   if (read(control_pipe_in_, &command, 1)) | ||||
|     return false; | ||||
|  | ||||
|   switch (command) { | ||||
|   case kCommandQuit: | ||||
|     return false; | ||||
|   default: | ||||
|     assert(0); | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool | ||||
| CrashGenerationServer::MakeMinidumpFilename(string& outFilename) | ||||
| { | ||||
|   GUID guid; | ||||
|   char guidString[kGUIDStringLength+1]; | ||||
|  | ||||
|   if (!(CreateGUID(&guid) | ||||
|         && GUIDToString(&guid, guidString, sizeof(guidString)))) | ||||
|     return false; | ||||
|  | ||||
|   char path[PATH_MAX]; | ||||
|   snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); | ||||
|  | ||||
|   outFilename = path; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // static | ||||
| void* | ||||
| CrashGenerationServer::ThreadMain(void *arg) | ||||
| { | ||||
|   reinterpret_cast<CrashGenerationServer*>(arg)->Run(); | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
| @@ -0,0 +1,135 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ | ||||
| #define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ | ||||
|  | ||||
| #include <pthread.h> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class ClientInfo; | ||||
|  | ||||
| class CrashGenerationServer { | ||||
| public: | ||||
|   // WARNING: callbacks may be invoked on a different thread | ||||
|   // than that which creates the CrashGenerationServer.  They must | ||||
|   // be thread safe. | ||||
|   typedef void (*OnClientDumpRequestCallback)(void* context, | ||||
|                                               const ClientInfo* client_info, | ||||
|                                               const string* file_path); | ||||
|  | ||||
|   typedef void (*OnClientExitingCallback)(void* context, | ||||
|                                           const ClientInfo* client_info); | ||||
|  | ||||
|   // Create an instance with the given parameters. | ||||
|   // | ||||
|   // Parameter listen_fd: The server fd created by CreateReportChannel(). | ||||
|   // Parameter dump_callback: Callback for a client crash dump request. | ||||
|   // Parameter dump_context: Context for client crash dump request callback. | ||||
|   // Parameter exit_callback: Callback for client process exit. | ||||
|   // Parameter exit_context: Context for client exit callback. | ||||
|   // Parameter generate_dumps: Whether to automatically generate dumps. | ||||
|   //     Client code of this class might want to generate dumps explicitly | ||||
|   //     in the crash dump request callback. In that case, false can be | ||||
|   //     passed for this parameter. | ||||
|   // Parameter dump_path: Path for generating dumps; required only if true is | ||||
|   //     passed for generateDumps parameter; NULL can be passed otherwise. | ||||
|   CrashGenerationServer(const int listen_fd, | ||||
|                         OnClientDumpRequestCallback dump_callback, | ||||
|                         void* dump_context, | ||||
|                         OnClientExitingCallback exit_callback, | ||||
|                         void* exit_context, | ||||
|                         bool generate_dumps, | ||||
|                         const string* dump_path); | ||||
|  | ||||
|   ~CrashGenerationServer(); | ||||
|  | ||||
|   // Perform initialization steps needed to start listening to clients. | ||||
|   // | ||||
|   // Return true if initialization is successful; false otherwise. | ||||
|   bool Start(); | ||||
|  | ||||
|   // Stop the server. | ||||
|   void Stop(); | ||||
|  | ||||
|   // Create a "channel" that can be used by clients to report crashes | ||||
|   // to a CrashGenerationServer.  |*server_fd| should be passed to | ||||
|   // this class's constructor, and |*client_fd| should be passed to | ||||
|   // the ExceptionHandler constructor in the client process. | ||||
|   static bool CreateReportChannel(int* server_fd, int* client_fd); | ||||
|  | ||||
| private: | ||||
|   // Run the server's event loop | ||||
|   void Run(); | ||||
|  | ||||
|   // Invoked when an child process (client) event occurs | ||||
|   // Returning true => "keep running", false => "exit loop" | ||||
|   bool ClientEvent(short revents); | ||||
|  | ||||
|   // Invoked when the controlling thread (main) event occurs | ||||
|   // Returning true => "keep running", false => "exit loop" | ||||
|   bool ControlEvent(short revents); | ||||
|  | ||||
|   // Return a unique filename at which a minidump can be written | ||||
|   bool MakeMinidumpFilename(string& outFilename); | ||||
|  | ||||
|   // Trampoline to |Run()| | ||||
|   static void* ThreadMain(void* arg); | ||||
|  | ||||
|   int server_fd_; | ||||
|  | ||||
|   OnClientDumpRequestCallback dump_callback_; | ||||
|   void* dump_context_; | ||||
|  | ||||
|   OnClientExitingCallback exit_callback_; | ||||
|   void* exit_context_; | ||||
|  | ||||
|   bool generate_dumps_; | ||||
|  | ||||
|   string dump_dir_; | ||||
|  | ||||
|   bool started_; | ||||
|  | ||||
|   pthread_t thread_; | ||||
|   int control_pipe_in_; | ||||
|   int control_pipe_out_; | ||||
|  | ||||
|   // disable these | ||||
|   CrashGenerationServer(const CrashGenerationServer&); | ||||
|   CrashGenerationServer& operator=(const CrashGenerationServer&); | ||||
| }; | ||||
|  | ||||
| } // namespace google_breakpad | ||||
|  | ||||
| #endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ | ||||
							
								
								
									
										3
									
								
								sdk/breakpad/client/linux/data/linux-gate-amd.sym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								sdk/breakpad/client/linux/data/linux-gate-amd.sym
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so | ||||
| PUBLIC 400 0 __kernel_vsyscall | ||||
| STACK WIN 4 400 100 1 1 0 0 0 0 0 1 | ||||
							
								
								
									
										3
									
								
								sdk/breakpad/client/linux/data/linux-gate-intel.sym
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								sdk/breakpad/client/linux/data/linux-gate-intel.sym
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so | ||||
| PUBLIC 400 0 __kernel_vsyscall | ||||
| STACK WIN 4 400 200 3 3 0 0 0 0 0 1 | ||||
							
								
								
									
										697
									
								
								sdk/breakpad/client/linux/handler/exception_handler.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										697
									
								
								sdk/breakpad/client/linux/handler/exception_handler.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,697 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // The ExceptionHandler object installs signal handlers for a number of | ||||
| // signals. We rely on the signal handler running on the thread which crashed | ||||
| // in order to identify it. This is true of the synchronous signals (SEGV etc), | ||||
| // but not true of ABRT. Thus, if you send ABRT to yourself in a program which | ||||
| // uses ExceptionHandler, you need to use tgkill to direct it to the current | ||||
| // thread. | ||||
| // | ||||
| // The signal flow looks like this: | ||||
| // | ||||
| //   SignalHandler (uses a global stack of ExceptionHandler objects to find | ||||
| //        |         one to handle the signal. If the first rejects it, try | ||||
| //        |         the second etc...) | ||||
| //        V | ||||
| //   HandleSignal ----------------------------| (clones a new process which | ||||
| //        |                                   |  shares an address space with | ||||
| //   (wait for cloned                         |  the crashed process. This | ||||
| //     process)                               |  allows us to ptrace the crashed | ||||
| //        |                                   |  process) | ||||
| //        V                                   V | ||||
| //   (set signal handler to             ThreadEntry (static function to bounce | ||||
| //    SIG_DFL and rethrow,                    |      back into the object) | ||||
| //    killing the crashed                     | | ||||
| //    process)                                V | ||||
| //                                          DoDump  (writes minidump) | ||||
| //                                            | | ||||
| //                                            V | ||||
| //                                         sys_exit | ||||
| // | ||||
|  | ||||
| // This code is a little fragmented. Different functions of the ExceptionHandler | ||||
| // class run in a number of different contexts. Some of them run in a normal | ||||
| // context and are easy to code, others run in a compromised context and the | ||||
| // restrictions at the top of minidump_writer.cc apply: no libc and use the | ||||
| // alternative malloc. Each function should have comment above it detailing the | ||||
| // context which it runs in. | ||||
|  | ||||
| #include "client/linux/handler/exception_handler.h" | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <linux/limits.h> | ||||
| #include <sched.h> | ||||
| #include <signal.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/prctl.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <sys/wait.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <sys/signal.h> | ||||
| #include <sys/ucontext.h> | ||||
| #include <sys/user.h> | ||||
| #include <ucontext.h> | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/basictypes.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/memory.h" | ||||
| #include "client/linux/log/log.h" | ||||
| #include "client/linux/minidump_writer/linux_dumper.h" | ||||
| #include "client/linux/minidump_writer/minidump_writer.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| #if defined(__ANDROID__) | ||||
| #include "linux/sched.h" | ||||
| #endif | ||||
|  | ||||
| #ifndef PR_SET_PTRACER | ||||
| #define PR_SET_PTRACER 0x59616d61 | ||||
| #endif | ||||
|  | ||||
| // A wrapper for the tgkill syscall: send a signal to a specific thread. | ||||
| static int tgkill(pid_t tgid, pid_t tid, int sig) { | ||||
|   return syscall(__NR_tgkill, tgid, tid, sig); | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| namespace { | ||||
| // The list of signals which we consider to be crashes. The default action for | ||||
| // all these signals must be Core (see man 7 signal) because we rethrow the | ||||
| // signal after handling it and expect that it'll be fatal. | ||||
| const int kExceptionSignals[] = { | ||||
|   SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS | ||||
| }; | ||||
| const int kNumHandledSignals = | ||||
|     sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); | ||||
| struct sigaction old_handlers[kNumHandledSignals]; | ||||
| bool handlers_installed = false; | ||||
|  | ||||
| // InstallAlternateStackLocked will store the newly installed stack in new_stack | ||||
| // and (if it exists) the previously installed stack in old_stack. | ||||
| stack_t old_stack; | ||||
| stack_t new_stack; | ||||
| bool stack_installed = false; | ||||
|  | ||||
| // Create an alternative stack to run the signal handlers on. This is done since | ||||
| // the signal might have been caused by a stack overflow. | ||||
| // Runs before crashing: normal context. | ||||
| void InstallAlternateStackLocked() { | ||||
|   if (stack_installed) | ||||
|     return; | ||||
|  | ||||
|   memset(&old_stack, 0, sizeof(old_stack)); | ||||
|   memset(&new_stack, 0, sizeof(new_stack)); | ||||
|  | ||||
|   // SIGSTKSZ may be too small to prevent the signal handlers from overrunning | ||||
|   // the alternative stack. Ensure that the size of the alternative stack is | ||||
|   // large enough. | ||||
|   static const unsigned kSigStackSize = std::max(8192, SIGSTKSZ); | ||||
|  | ||||
|   // Only set an alternative stack if there isn't already one, or if the current | ||||
|   // one is too small. | ||||
|   if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || | ||||
|       old_stack.ss_size < kSigStackSize) { | ||||
|     new_stack.ss_sp = malloc(kSigStackSize); | ||||
|     new_stack.ss_size = kSigStackSize; | ||||
|  | ||||
|     if (sys_sigaltstack(&new_stack, NULL) == -1) { | ||||
|       free(new_stack.ss_sp); | ||||
|       return; | ||||
|     } | ||||
|     stack_installed = true; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Runs before crashing: normal context. | ||||
| void RestoreAlternateStackLocked() { | ||||
|   if (!stack_installed) | ||||
|     return; | ||||
|  | ||||
|   stack_t current_stack; | ||||
|   if (sys_sigaltstack(NULL, ¤t_stack) == -1) | ||||
|     return; | ||||
|  | ||||
|   // Only restore the old_stack if the current alternative stack is the one | ||||
|   // installed by the call to InstallAlternateStackLocked. | ||||
|   if (current_stack.ss_sp == new_stack.ss_sp) { | ||||
|     if (old_stack.ss_sp) { | ||||
|       if (sys_sigaltstack(&old_stack, NULL) == -1) | ||||
|         return; | ||||
|     } else { | ||||
|       stack_t disable_stack; | ||||
|       disable_stack.ss_flags = SS_DISABLE; | ||||
|       if (sys_sigaltstack(&disable_stack, NULL) == -1) | ||||
|         return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   free(new_stack.ss_sp); | ||||
|   stack_installed = false; | ||||
| } | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| // We can stack multiple exception handlers. In that case, this is the global | ||||
| // which holds the stack. | ||||
| std::vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL; | ||||
| pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = | ||||
|     PTHREAD_MUTEX_INITIALIZER; | ||||
|  | ||||
| // Runs before crashing: normal context. | ||||
| ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, | ||||
|                                    FilterCallback filter, | ||||
|                                    MinidumpCallback callback, | ||||
|                                    void* callback_context, | ||||
|                                    bool install_handler, | ||||
|                                    const int server_fd) | ||||
|     : filter_(filter), | ||||
|       callback_(callback), | ||||
|       callback_context_(callback_context), | ||||
|       minidump_descriptor_(descriptor), | ||||
|       crash_handler_(NULL) { | ||||
|   if (server_fd >= 0) | ||||
|     crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); | ||||
|  | ||||
|   if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) | ||||
|     minidump_descriptor_.UpdatePath(); | ||||
|  | ||||
|   pthread_mutex_lock(&handler_stack_mutex_); | ||||
|   if (!handler_stack_) | ||||
|     handler_stack_ = new std::vector<ExceptionHandler*>; | ||||
|   if (install_handler) { | ||||
|     InstallAlternateStackLocked(); | ||||
|     InstallHandlersLocked(); | ||||
|   } | ||||
|   handler_stack_->push_back(this); | ||||
|   pthread_mutex_unlock(&handler_stack_mutex_); | ||||
| } | ||||
|  | ||||
| // Runs before crashing: normal context. | ||||
| ExceptionHandler::~ExceptionHandler() { | ||||
|   pthread_mutex_lock(&handler_stack_mutex_); | ||||
|   std::vector<ExceptionHandler*>::iterator handler = | ||||
|       std::find(handler_stack_->begin(), handler_stack_->end(), this); | ||||
|   handler_stack_->erase(handler); | ||||
|   if (handler_stack_->empty()) { | ||||
|     RestoreAlternateStackLocked(); | ||||
|     RestoreHandlersLocked(); | ||||
|   } | ||||
|   pthread_mutex_unlock(&handler_stack_mutex_); | ||||
| } | ||||
|  | ||||
| // Runs before crashing: normal context. | ||||
| // static | ||||
| bool ExceptionHandler::InstallHandlersLocked() { | ||||
|   if (handlers_installed) | ||||
|     return false; | ||||
|  | ||||
|   // Fail if unable to store all the old handlers. | ||||
|   for (int i = 0; i < kNumHandledSignals; ++i) { | ||||
|     if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1) | ||||
|       return false; | ||||
|   } | ||||
|  | ||||
|   struct sigaction sa; | ||||
|   memset(&sa, 0, sizeof(sa)); | ||||
|   sigemptyset(&sa.sa_mask); | ||||
|  | ||||
|   // Mask all exception signals when we're handling one of them. | ||||
|   for (int i = 0; i < kNumHandledSignals; ++i) | ||||
|     sigaddset(&sa.sa_mask, kExceptionSignals[i]); | ||||
|  | ||||
|   sa.sa_sigaction = SignalHandler; | ||||
|   sa.sa_flags = SA_ONSTACK | SA_SIGINFO; | ||||
|  | ||||
|   for (int i = 0; i < kNumHandledSignals; ++i) { | ||||
|     if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) { | ||||
|       // At this point it is impractical to back out changes, and so failure to | ||||
|       // install a signal is intentionally ignored. | ||||
|     } | ||||
|   } | ||||
|   handlers_installed = true; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // This function runs in a compromised context: see the top of the file. | ||||
| // Runs on the crashing thread. | ||||
| // static | ||||
| void ExceptionHandler::RestoreHandlersLocked() { | ||||
|   if (!handlers_installed) | ||||
|     return; | ||||
|  | ||||
|   for (int i = 0; i < kNumHandledSignals; ++i) { | ||||
|     if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) { | ||||
|       signal(kExceptionSignals[i], SIG_DFL); | ||||
|     } | ||||
|   } | ||||
|   handlers_installed = false; | ||||
| } | ||||
|  | ||||
| // void ExceptionHandler::set_crash_handler(HandlerCallback callback) { | ||||
| //   crash_handler_ = callback; | ||||
| // } | ||||
|  | ||||
| // This function runs in a compromised context: see the top of the file. | ||||
| // Runs on the crashing thread. | ||||
| // static | ||||
| void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { | ||||
|   // All the exception signals are blocked at this point. | ||||
|   pthread_mutex_lock(&handler_stack_mutex_); | ||||
|  | ||||
|   // Sometimes, Breakpad runs inside a process where some other buggy code | ||||
|   // saves and restores signal handlers temporarily with 'signal' | ||||
|   // instead of 'sigaction'. This loses the SA_SIGINFO flag associated | ||||
|   // with this function. As a consequence, the values of 'info' and 'uc' | ||||
|   // become totally bogus, generally inducing a crash. | ||||
|   // | ||||
|   // The following code tries to detect this case. When it does, it | ||||
|   // resets the signal handlers with sigaction + SA_SIGINFO and returns. | ||||
|   // This forces the signal to be thrown again, but this time the kernel | ||||
|   // will call the function with the right arguments. | ||||
|   struct sigaction cur_handler; | ||||
|   if (sigaction(sig, NULL, &cur_handler) == 0 && | ||||
|       (cur_handler.sa_flags & SA_SIGINFO) == 0) { | ||||
|     // Reset signal handler with the right flags. | ||||
|     sigemptyset(&cur_handler.sa_mask); | ||||
|     sigaddset(&cur_handler.sa_mask, sig); | ||||
|  | ||||
|     cur_handler.sa_sigaction = SignalHandler; | ||||
|     cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO; | ||||
|  | ||||
|     if (sigaction(sig, &cur_handler, NULL) == -1) { | ||||
|       // When resetting the handler fails, try to reset the | ||||
|       // default one to avoid an infinite loop here. | ||||
|       signal(sig, SIG_DFL); | ||||
|     } | ||||
|     pthread_mutex_unlock(&handler_stack_mutex_); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   bool handled = false; | ||||
|   for (int i = handler_stack_->size() - 1; !handled && i >= 0; --i) { | ||||
|     handled = (*handler_stack_)[i]->HandleSignal(sig, info, uc); | ||||
|   } | ||||
|  | ||||
|   // Upon returning from this signal handler, sig will become unmasked and then | ||||
|   // it will be retriggered. If one of the ExceptionHandlers handled it | ||||
|   // successfully, restore the default handler. Otherwise, restore the | ||||
|   // previously installed handler. Then, when the signal is retriggered, it will | ||||
|   // be delivered to the appropriate handler. | ||||
|   if (handled) { | ||||
|     signal(sig, SIG_DFL); | ||||
|   } else { | ||||
|     RestoreHandlersLocked(); | ||||
|   } | ||||
|  | ||||
|   pthread_mutex_unlock(&handler_stack_mutex_); | ||||
|  | ||||
|   if (info->si_pid || sig == SIGABRT) { | ||||
|     // This signal was triggered by somebody sending us the signal with kill(). | ||||
|     // In order to retrigger it, we have to queue a new signal by calling | ||||
|     // kill() ourselves.  The special case (si_pid == 0 && sig == SIGABRT) is | ||||
|     // due to the kernel sending a SIGABRT from a user request via SysRQ. | ||||
|     if (tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { | ||||
|       // If we failed to kill ourselves (e.g. because a sandbox disallows us | ||||
|       // to do so), we instead resort to terminating our process. This will | ||||
|       // result in an incorrect exit code. | ||||
|       _exit(1); | ||||
|     } | ||||
|   } else { | ||||
|     // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV). | ||||
|     // No need to reissue the signal. It will automatically trigger again, | ||||
|     // when we return from the signal handler. | ||||
|   } | ||||
| } | ||||
|  | ||||
| struct ThreadArgument { | ||||
|   pid_t pid;  // the crashing process | ||||
|   const MinidumpDescriptor* minidump_descriptor; | ||||
|   ExceptionHandler* handler; | ||||
|   const void* context;  // a CrashContext structure | ||||
|   size_t context_size; | ||||
| }; | ||||
|  | ||||
| // This is the entry function for the cloned process. We are in a compromised | ||||
| // context here: see the top of the file. | ||||
| // static | ||||
| int ExceptionHandler::ThreadEntry(void *arg) { | ||||
|   const ThreadArgument *thread_arg = reinterpret_cast<ThreadArgument*>(arg); | ||||
|  | ||||
|   // Block here until the crashing process unblocks us when | ||||
|   // we're allowed to use ptrace | ||||
|   thread_arg->handler->WaitForContinueSignal(); | ||||
|  | ||||
|   return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, | ||||
|                                      thread_arg->context_size) == false; | ||||
| } | ||||
|  | ||||
| // This function runs in a compromised context: see the top of the file. | ||||
| // Runs on the crashing thread. | ||||
| bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { | ||||
|   if (filter_ && !filter_(callback_context_)) | ||||
|     return false; | ||||
|  | ||||
|   // Allow ourselves to be dumped if the signal is trusted. | ||||
|   bool signal_trusted = info->si_code > 0; | ||||
|   bool signal_pid_trusted = info->si_code == SI_USER || | ||||
|       info->si_code == SI_TKILL; | ||||
|   if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { | ||||
|     sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); | ||||
|   } | ||||
|   CrashContext context; | ||||
|   memcpy(&context.siginfo, info, sizeof(siginfo_t)); | ||||
|   memcpy(&context.context, uc, sizeof(struct ucontext)); | ||||
| #if !defined(__ARM_EABI__) && !defined(__mips__) | ||||
|   // FP state is not part of user ABI on ARM Linux. | ||||
|   // In case of MIPS Linux FP state is already part of struct ucontext | ||||
|   // and 'float_state' is not a member of CrashContext. | ||||
|   struct ucontext *uc_ptr = (struct ucontext*)uc; | ||||
|   if (uc_ptr->uc_mcontext.fpregs) { | ||||
|     memcpy(&context.float_state, | ||||
|            uc_ptr->uc_mcontext.fpregs, | ||||
|            sizeof(context.float_state)); | ||||
|   } | ||||
| #endif | ||||
|   context.tid = syscall(__NR_gettid); | ||||
|   if (crash_handler_ != NULL) { | ||||
|     if (crash_handler_(&context, sizeof(context), callback_context_)) { | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
|   return GenerateDump(&context); | ||||
| } | ||||
|  | ||||
| // This is a public interface to HandleSignal that allows the client to | ||||
| // generate a crash dump. This function may run in a compromised context. | ||||
| bool ExceptionHandler::SimulateSignalDelivery(int sig) { | ||||
|   siginfo_t siginfo = {}; | ||||
|   // Mimic a trusted signal to allow tracing the process (see | ||||
|   // ExceptionHandler::HandleSignal(). | ||||
|   siginfo.si_code = SI_USER; | ||||
|   siginfo.si_pid = getpid(); | ||||
|   struct ucontext context; | ||||
|   getcontext(&context); | ||||
|   return HandleSignal(sig, &siginfo, &context); | ||||
| } | ||||
|  | ||||
| // This function may run in a compromised context: see the top of the file. | ||||
| bool ExceptionHandler::GenerateDump(CrashContext *context) { | ||||
|   if (IsOutOfProcess()) | ||||
|     return crash_generation_client_->RequestDump(context, sizeof(*context)); | ||||
|  | ||||
|   // Allocating too much stack isn't a problem, and better to err on the side | ||||
|   // of caution than smash it into random locations. | ||||
|   static const unsigned kChildStackSize = 16000; | ||||
|   PageAllocator allocator; | ||||
|   uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); | ||||
|   if (!stack) | ||||
|     return false; | ||||
|   // clone() needs the top-most address. (scrub just to be safe) | ||||
|   stack += kChildStackSize; | ||||
|   my_memset(stack - 16, 0, 16); | ||||
|  | ||||
|   ThreadArgument thread_arg; | ||||
|   thread_arg.handler = this; | ||||
|   thread_arg.minidump_descriptor = &minidump_descriptor_; | ||||
|   thread_arg.pid = getpid(); | ||||
|   thread_arg.context = context; | ||||
|   thread_arg.context_size = sizeof(*context); | ||||
|  | ||||
|   // We need to explicitly enable ptrace of parent processes on some | ||||
|   // kernels, but we need to know the PID of the cloned process before we | ||||
|   // can do this. Create a pipe here which we can use to block the | ||||
|   // cloned process after creating it, until we have explicitly enabled ptrace | ||||
|   if(sys_pipe(fdes) == -1) { | ||||
|     // Creating the pipe failed. We'll log an error but carry on anyway, | ||||
|     // as we'll probably still get a useful crash report. All that will happen | ||||
|     // is the write() and read() calls will fail with EBADF | ||||
|     static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump \ | ||||
|                                        sys_pipe failed:"; | ||||
|     logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); | ||||
|     logger::write(strerror(errno), strlen(strerror(errno))); | ||||
|     logger::write("\n", 1); | ||||
|   } | ||||
|  | ||||
|   const pid_t child = sys_clone( | ||||
|       ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, | ||||
|       &thread_arg, NULL, NULL, NULL); | ||||
|  | ||||
|   int r, status; | ||||
|   // Allow the child to ptrace us | ||||
|   sys_prctl(PR_SET_PTRACER, child, 0, 0, 0); | ||||
|   SendContinueSignalToChild(); | ||||
|   do { | ||||
|     r = sys_waitpid(child, &status, __WALL); | ||||
|   } while (r == -1 && errno == EINTR); | ||||
|  | ||||
|   sys_close(fdes[0]); | ||||
|   sys_close(fdes[1]); | ||||
|  | ||||
|   if (r == -1) { | ||||
|     static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; | ||||
|     logger::write(msg, sizeof(msg) - 1); | ||||
|     logger::write(strerror(errno), strlen(strerror(errno))); | ||||
|     logger::write("\n", 1); | ||||
|   } | ||||
|  | ||||
|   bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; | ||||
|   if (callback_) | ||||
|     success = callback_(minidump_descriptor_, callback_context_, success); | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| // This function runs in a compromised context: see the top of the file. | ||||
| void ExceptionHandler::SendContinueSignalToChild() { | ||||
|   static const char okToContinueMessage = 'a'; | ||||
|   int r; | ||||
|   r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); | ||||
|   if(r == -1) { | ||||
|     static const char msg[] = "ExceptionHandler::SendContinueSignalToChild \ | ||||
|                                sys_write failed:"; | ||||
|     logger::write(msg, sizeof(msg) - 1); | ||||
|     logger::write(strerror(errno), strlen(strerror(errno))); | ||||
|     logger::write("\n", 1); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // This function runs in a compromised context: see the top of the file. | ||||
| // Runs on the cloned process. | ||||
| void ExceptionHandler::WaitForContinueSignal() { | ||||
|   int r; | ||||
|   char receivedMessage; | ||||
|   r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); | ||||
|   if(r == -1) { | ||||
|     static const char msg[] = "ExceptionHandler::WaitForContinueSignal \ | ||||
|                                sys_read failed:"; | ||||
|     logger::write(msg, sizeof(msg) - 1); | ||||
|     logger::write(strerror(errno), strlen(strerror(errno))); | ||||
|     logger::write("\n", 1); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // This function runs in a compromised context: see the top of the file. | ||||
| // Runs on the cloned process. | ||||
| bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, | ||||
|                               size_t context_size) { | ||||
|   if (minidump_descriptor_.IsFD()) { | ||||
|     return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), | ||||
|                                           minidump_descriptor_.size_limit(), | ||||
|                                           crashing_process, | ||||
|                                           context, | ||||
|                                           context_size, | ||||
|                                           mapping_list_, | ||||
|                                           app_memory_list_); | ||||
|   } | ||||
|   return google_breakpad::WriteMinidump(minidump_descriptor_.path(), | ||||
|                                         minidump_descriptor_.size_limit(), | ||||
|                                         crashing_process, | ||||
|                                         context, | ||||
|                                         context_size, | ||||
|                                         mapping_list_, | ||||
|                                         app_memory_list_); | ||||
| } | ||||
|  | ||||
| // static | ||||
| bool ExceptionHandler::WriteMinidump(const string& dump_path, | ||||
|                                      MinidumpCallback callback, | ||||
|                                      void* callback_context) { | ||||
|   MinidumpDescriptor descriptor(dump_path); | ||||
|   ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); | ||||
|   return eh.WriteMinidump(); | ||||
| } | ||||
|  | ||||
| // In order to making using EBP to calculate the desired value for ESP | ||||
| // a valid operation, ensure that this function is compiled with a | ||||
| // frame pointer using the following attribute. This attribute | ||||
| // is supported on GCC but not on clang. | ||||
| #if defined(__i386__) && defined(__GNUC__) && !defined(__clang__) | ||||
| __attribute__((optimize("no-omit-frame-pointer"))) | ||||
| #endif | ||||
| bool ExceptionHandler::WriteMinidump() { | ||||
|   if (!IsOutOfProcess() && !minidump_descriptor_.IsFD()) { | ||||
|     // Update the path of the minidump so that this can be called multiple times | ||||
|     // and new files are created for each minidump.  This is done before the | ||||
|     // generation happens, as clients may want to access the MinidumpDescriptor | ||||
|     // after this call to find the exact path to the minidump file. | ||||
|     minidump_descriptor_.UpdatePath(); | ||||
|   } else if (minidump_descriptor_.IsFD()) { | ||||
|     // Reposition the FD to its beginning and resize it to get rid of the | ||||
|     // previous minidump info. | ||||
|     lseek(minidump_descriptor_.fd(), 0, SEEK_SET); | ||||
|     ignore_result(ftruncate(minidump_descriptor_.fd(), 0)); | ||||
|   } | ||||
|  | ||||
|   // Allow this process to be dumped. | ||||
|   sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); | ||||
|  | ||||
|   CrashContext context; | ||||
|   int getcontext_result = getcontext(&context.context); | ||||
|   if (getcontext_result) | ||||
|     return false; | ||||
|  | ||||
| #if defined(__i386__) | ||||
|   // In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved | ||||
|   // from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer | ||||
|   // and it only makes sense when running in kernel mode with a different stack | ||||
|   // pointer. When WriteMiniDump is called during normal processing REG_UESP is | ||||
|   // zero which leads to bad minidump files. | ||||
|   if (!context.context.uc_mcontext.gregs[REG_UESP]) { | ||||
|     // If REG_UESP is set to REG_ESP then that includes the stack space for the | ||||
|     // CrashContext object in this function, which is about 128 KB. Since the | ||||
|     // Linux dumper only records 32 KB of stack this would mean that nothing | ||||
|     // useful would be recorded. A better option is to set REG_UESP to REG_EBP, | ||||
|     // perhaps with a small negative offset in case there is any code that | ||||
|     // objects to them being equal. | ||||
|     context.context.uc_mcontext.gregs[REG_UESP] = | ||||
|       context.context.uc_mcontext.gregs[REG_EBP] - 16; | ||||
|     // The stack saving is based off of REG_ESP so it must be set to match the | ||||
|     // new REG_UESP. | ||||
|     context.context.uc_mcontext.gregs[REG_ESP] = | ||||
|       context.context.uc_mcontext.gregs[REG_UESP]; | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #if !defined(__ARM_EABI__) && !defined(__mips__) | ||||
|   // FPU state is not part of ARM EABI ucontext_t. | ||||
|   memcpy(&context.float_state, context.context.uc_mcontext.fpregs, | ||||
|          sizeof(context.float_state)); | ||||
| #endif | ||||
|   context.tid = sys_gettid(); | ||||
|  | ||||
|   // Add an exception stream to the minidump for better reporting. | ||||
|   memset(&context.siginfo, 0, sizeof(context.siginfo)); | ||||
|   context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; | ||||
| #if defined(__i386__) | ||||
|   context.siginfo.si_addr = | ||||
|       reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_EIP]); | ||||
| #elif defined(__x86_64__) | ||||
|   context.siginfo.si_addr = | ||||
|       reinterpret_cast<void*>(context.context.uc_mcontext.gregs[REG_RIP]); | ||||
| #elif defined(__arm__) | ||||
|   context.siginfo.si_addr = | ||||
|       reinterpret_cast<void*>(context.context.uc_mcontext.arm_pc); | ||||
| #elif defined(__mips__) | ||||
|   context.siginfo.si_addr = | ||||
|       reinterpret_cast<void*>(context.context.uc_mcontext.pc); | ||||
| #else | ||||
| #error "This code has not been ported to your platform yet." | ||||
| #endif | ||||
|  | ||||
|   return GenerateDump(&context); | ||||
| } | ||||
|  | ||||
| void ExceptionHandler::AddMappingInfo(const string& name, | ||||
|                                       const uint8_t identifier[sizeof(MDGUID)], | ||||
|                                       uintptr_t start_address, | ||||
|                                       size_t mapping_size, | ||||
|                                       size_t file_offset) { | ||||
|   MappingInfo info; | ||||
|   info.start_addr = start_address; | ||||
|   info.size = mapping_size; | ||||
|   info.offset = file_offset; | ||||
|   strncpy(info.name, name.c_str(), sizeof(info.name) - 1); | ||||
|   info.name[sizeof(info.name) - 1] = '\0'; | ||||
|  | ||||
|   MappingEntry mapping; | ||||
|   mapping.first = info; | ||||
|   memcpy(mapping.second, identifier, sizeof(MDGUID)); | ||||
|   mapping_list_.push_back(mapping); | ||||
| } | ||||
|  | ||||
| void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { | ||||
|   AppMemoryList::iterator iter = | ||||
|     std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); | ||||
|   if (iter != app_memory_list_.end()) { | ||||
|     // Don't allow registering the same pointer twice. | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   AppMemory app_memory; | ||||
|   app_memory.ptr = ptr; | ||||
|   app_memory.length = length; | ||||
|   app_memory_list_.push_back(app_memory); | ||||
| } | ||||
|  | ||||
| void ExceptionHandler::UnregisterAppMemory(void* ptr) { | ||||
|   AppMemoryList::iterator iter = | ||||
|     std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); | ||||
|   if (iter != app_memory_list_.end()) { | ||||
|     app_memory_list_.erase(iter); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // static | ||||
| bool ExceptionHandler::WriteMinidumpForChild(pid_t child, | ||||
|                                              pid_t child_blamed_thread, | ||||
|                                              const string& dump_path, | ||||
|                                              MinidumpCallback callback, | ||||
|                                              void* callback_context) { | ||||
|   // This function is not run in a compromised context. | ||||
|   MinidumpDescriptor descriptor(dump_path); | ||||
|   descriptor.UpdatePath(); | ||||
|   if (!google_breakpad::WriteMinidump(descriptor.path(), | ||||
|                                       child, | ||||
|                                       child_blamed_thread)) | ||||
|       return false; | ||||
|  | ||||
|   return callback ? callback(descriptor, callback_context, true) : true; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										275
									
								
								sdk/breakpad/client/linux/handler/exception_handler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								sdk/breakpad/client/linux/handler/exception_handler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ | ||||
| #define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <signal.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <sys/ucontext.h> | ||||
|  | ||||
| #include "client/linux/crash_generation/crash_generation_client.h" | ||||
| #include "client/linux/handler/minidump_descriptor.h" | ||||
| #include "client/linux/minidump_writer/minidump_writer.h" | ||||
| #include "common/scoped_ptr.h" | ||||
| #include "common/using_std_string.h" | ||||
| #include "google_breakpad/common/minidump_format.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // ExceptionHandler | ||||
| // | ||||
| // ExceptionHandler can write a minidump file when an exception occurs, | ||||
| // or when WriteMinidump() is called explicitly by your program. | ||||
| // | ||||
| // To have the exception handler write minidumps when an uncaught exception | ||||
| // (crash) occurs, you should create an instance early in the execution | ||||
| // of your program, and keep it around for the entire time you want to | ||||
| // have crash handling active (typically, until shutdown). | ||||
| // (NOTE): There should be only be one this kind of exception handler | ||||
| // object per process. | ||||
| // | ||||
| // If you want to write minidumps without installing the exception handler, | ||||
| // you can create an ExceptionHandler with install_handler set to false, | ||||
| // then call WriteMinidump.  You can also use this technique if you want to | ||||
| // use different minidump callbacks for different call sites. | ||||
| // | ||||
| // In either case, a callback function is called when a minidump is written, | ||||
| // which receives the full path or file descriptor of the minidump.  The | ||||
| // caller can collect and write additional application state to that minidump, | ||||
| // and launch an external crash-reporting application. | ||||
| // | ||||
| // Caller should try to make the callbacks as crash-friendly as possible, | ||||
| // it should avoid use heap memory allocation as much as possible. | ||||
|  | ||||
| class ExceptionHandler { | ||||
|  public: | ||||
|   // A callback function to run before Breakpad performs any substantial | ||||
|   // processing of an exception.  A FilterCallback is called before writing | ||||
|   // a minidump.  |context| is the parameter supplied by the user as | ||||
|   // callback_context when the handler was created. | ||||
|   // | ||||
|   // If a FilterCallback returns true, Breakpad will continue processing, | ||||
|   // attempting to write a minidump.  If a FilterCallback returns false, | ||||
|   // Breakpad  will immediately report the exception as unhandled without | ||||
|   // writing a minidump, allowing another handler the opportunity to handle it. | ||||
|   typedef bool (*FilterCallback)(void *context); | ||||
|  | ||||
|   // A callback function to run after the minidump has been written. | ||||
|   // |descriptor| contains the file descriptor or file path containing the | ||||
|   // minidump. |context| is the parameter supplied by the user as | ||||
|   // callback_context when the handler was created.  |succeeded| indicates | ||||
|   // whether a minidump file was successfully written. | ||||
|   // | ||||
|   // If an exception occurred and the callback returns true, Breakpad will | ||||
|   // treat the exception as fully-handled, suppressing any other handlers from | ||||
|   // being notified of the exception.  If the callback returns false, Breakpad | ||||
|   // will treat the exception as unhandled, and allow another handler to handle | ||||
|   // it. If there are no other handlers, Breakpad will report the exception to | ||||
|   // the system as unhandled, allowing a debugger or native crash dialog the | ||||
|   // opportunity to handle the exception.  Most callback implementations | ||||
|   // should normally return the value of |succeeded|, or when they wish to | ||||
|   // not report an exception of handled, false.  Callbacks will rarely want to | ||||
|   // return true directly (unless |succeeded| is true). | ||||
|   typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor, | ||||
|                                    void* context, | ||||
|                                    bool succeeded); | ||||
|  | ||||
|   // In certain cases, a user may wish to handle the generation of the minidump | ||||
|   // themselves. In this case, they can install a handler callback which is | ||||
|   // called when a crash has occurred. If this function returns true, no other | ||||
|   // processing of occurs and the process will shortly be crashed. If this | ||||
|   // returns false, the normal processing continues. | ||||
|   typedef bool (*HandlerCallback)(const void* crash_context, | ||||
|                                   size_t crash_context_size, | ||||
|                                   void* context); | ||||
|  | ||||
|   // Creates a new ExceptionHandler instance to handle writing minidumps. | ||||
|   // Before writing a minidump, the optional |filter| callback will be called. | ||||
|   // Its return value determines whether or not Breakpad should write a | ||||
|   // minidump.  The minidump content will be written to the file path or file | ||||
|   // descriptor from |descriptor|, and the optional |callback| is called after | ||||
|   // writing the dump file, as described above. | ||||
|   // If install_handler is true, then a minidump will be written whenever | ||||
|   // an unhandled exception occurs.  If it is false, minidumps will only | ||||
|   // be written when WriteMinidump is called. | ||||
|   // If |server_fd| is valid, the minidump is generated out-of-process.  If it | ||||
|   // is -1, in-process generation will always be used. | ||||
|   ExceptionHandler(const MinidumpDescriptor& descriptor, | ||||
|                    FilterCallback filter, | ||||
|                    MinidumpCallback callback, | ||||
|                    void *callback_context, | ||||
|                    bool install_handler, | ||||
|                    const int server_fd); | ||||
|   ~ExceptionHandler(); | ||||
|  | ||||
|   const MinidumpDescriptor& minidump_descriptor() const { | ||||
|     return minidump_descriptor_; | ||||
|   } | ||||
|  | ||||
|   void set_minidump_descriptor(const MinidumpDescriptor& descriptor) { | ||||
|     minidump_descriptor_ = descriptor; | ||||
|   } | ||||
|  | ||||
|   void set_crash_handler(HandlerCallback callback) { | ||||
|     crash_handler_ = callback; | ||||
|   } | ||||
|  | ||||
|   // Writes a minidump immediately.  This can be used to capture the execution | ||||
|   // state independently of a crash. | ||||
|   // Returns true on success. | ||||
|   // If the ExceptionHandler has been created with a path, a new file is | ||||
|   // generated for each minidump.  The file path can be retrieved in the | ||||
|   // MinidumpDescriptor passed to the MinidumpCallback or by accessing the | ||||
|   // MinidumpDescriptor directly from the ExceptionHandler (with | ||||
|   // minidump_descriptor()). | ||||
|   // If the ExceptionHandler has been created with a file descriptor, the file | ||||
|   // descriptor is repositioned to its beginning and the previous generated | ||||
|   // minidump is overwritten. | ||||
|   // Note that this method is not supposed to be called from a compromised | ||||
|   // context as it uses the heap. | ||||
|   bool WriteMinidump(); | ||||
|  | ||||
|   // Convenience form of WriteMinidump which does not require an | ||||
|   // ExceptionHandler instance. | ||||
|   static bool WriteMinidump(const string& dump_path, | ||||
|                             MinidumpCallback callback, | ||||
|                             void* callback_context); | ||||
|  | ||||
|   // Write a minidump of |child| immediately.  This can be used to | ||||
|   // capture the execution state of |child| independently of a crash. | ||||
|   // Pass a meaningful |child_blamed_thread| to make that thread in | ||||
|   // the child process the one from which a crash signature is | ||||
|   // extracted. | ||||
|   // | ||||
|   // WARNING: the return of this function *must* happen before | ||||
|   // the code that will eventually reap |child| executes. | ||||
|   // Otherwise there's a pernicious race condition in which |child| | ||||
|   // exits, is reaped, another process created with its pid, then that | ||||
|   // new process dumped. | ||||
|   static bool WriteMinidumpForChild(pid_t child, | ||||
|                                     pid_t child_blamed_thread, | ||||
|                                     const string& dump_path, | ||||
|                                     MinidumpCallback callback, | ||||
|                                     void* callback_context); | ||||
|  | ||||
|   // This structure is passed to minidump_writer.h:WriteMinidump via an opaque | ||||
|   // blob. It shouldn't be needed in any user code. | ||||
|   struct CrashContext { | ||||
|     siginfo_t siginfo; | ||||
|     pid_t tid;  // the crashing thread. | ||||
|     struct ucontext context; | ||||
| #if !defined(__ARM_EABI__) && !defined(__mips__) | ||||
|     // #ifdef this out because FP state is not part of user ABI for Linux ARM. | ||||
|     // In case of MIPS Linux FP state is already part of struct ucontext | ||||
|     // so 'float_state' is not required. | ||||
|     struct _libc_fpstate float_state; | ||||
| #endif | ||||
|   }; | ||||
|  | ||||
|   // Returns whether out-of-process dump generation is used or not. | ||||
|   bool IsOutOfProcess() const { | ||||
|       return crash_generation_client_.get() != NULL; | ||||
|   } | ||||
|  | ||||
|   // Add information about a memory mapping. This can be used if | ||||
|   // a custom library loader is used that maps things in a way | ||||
|   // that the linux dumper can't handle by reading the maps file. | ||||
|   void AddMappingInfo(const string& name, | ||||
|                       const uint8_t identifier[sizeof(MDGUID)], | ||||
|                       uintptr_t start_address, | ||||
|                       size_t mapping_size, | ||||
|                       size_t file_offset); | ||||
|  | ||||
|   // Register a block of memory of length bytes starting at address ptr | ||||
|   // to be copied to the minidump when a crash happens. | ||||
|   void RegisterAppMemory(void* ptr, size_t length); | ||||
|  | ||||
|   // Unregister a block of memory that was registered with RegisterAppMemory. | ||||
|   void UnregisterAppMemory(void* ptr); | ||||
|  | ||||
|   // Force signal handling for the specified signal. | ||||
|   bool SimulateSignalDelivery(int sig); | ||||
|  private: | ||||
|   // Save the old signal handlers and install new ones. | ||||
|   static bool InstallHandlersLocked(); | ||||
|   // Restore the old signal handlers. | ||||
|   static void RestoreHandlersLocked(); | ||||
|  | ||||
|   void PreresolveSymbols(); | ||||
|   bool GenerateDump(CrashContext *context); | ||||
|   void SendContinueSignalToChild(); | ||||
|   void WaitForContinueSignal(); | ||||
|  | ||||
|   static void SignalHandler(int sig, siginfo_t* info, void* uc); | ||||
|   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_; | ||||
|  | ||||
|   scoped_ptr<CrashGenerationClient> crash_generation_client_; | ||||
|  | ||||
|   MinidumpDescriptor minidump_descriptor_; | ||||
|  | ||||
|   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_; | ||||
|   static pthread_mutex_t handler_stack_mutex_; | ||||
|  | ||||
|   // We need to explicitly enable ptrace of parent processes on some | ||||
|   // kernels, but we need to know the PID of the cloned process before we | ||||
|   // can do this. We create a pipe which we can use to block the | ||||
|   // cloned process after creating it, until we have explicitly enabled  | ||||
|   // ptrace. This is used to store the file descriptors for the pipe | ||||
|   int fdes[2]; | ||||
|  | ||||
|   // Callers can add extra info about mappings for cases where the | ||||
|   // dumper code cannot extract enough information from /proc/<pid>/maps. | ||||
|   MappingList mapping_list_; | ||||
|  | ||||
|   // Callers can request additional memory regions to be included in | ||||
|   // the dump. | ||||
|   AppMemoryList app_memory_list_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ | ||||
							
								
								
									
										1177
									
								
								sdk/breakpad/client/linux/handler/exception_handler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1177
									
								
								sdk/breakpad/client/linux/handler/exception_handler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										52
									
								
								sdk/breakpad/client/linux/handler/microdump_extra_info.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								sdk/breakpad/client/linux/handler/microdump_extra_info.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| // Copyright 2015 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ | ||||
| #define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| struct MicrodumpExtraInfo { | ||||
|   // Strings pointed to by this struct are not copied, and are | ||||
|   // expected to remain valid for the lifetime of the process. | ||||
|   const char* build_fingerprint; | ||||
|   const char* product_info; | ||||
|   const char* gpu_fingerprint; | ||||
|   const char* process_type; | ||||
|  | ||||
|   MicrodumpExtraInfo() | ||||
|       : build_fingerprint(NULL), | ||||
|         product_info(NULL), | ||||
|         gpu_fingerprint(NULL), | ||||
|         process_type(NULL) {} | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| #endif  // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ | ||||
							
								
								
									
										79
									
								
								sdk/breakpad/client/linux/handler/minidump_descriptor.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								sdk/breakpad/client/linux/handler/minidump_descriptor.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| // Copyright (c) 2012 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| #include "client/linux/handler/minidump_descriptor.h" | ||||
|  | ||||
| #include "common/linux/guid_creator.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor) | ||||
|     : fd_(descriptor.fd_), | ||||
|       directory_(descriptor.directory_), | ||||
|       c_path_(NULL), | ||||
|       size_limit_(descriptor.size_limit_) { | ||||
|   // The copy constructor is not allowed to be called on a MinidumpDescriptor | ||||
|   // with a valid path_, as getting its c_path_ would require the heap which | ||||
|   // can cause problems in compromised environments. | ||||
|   assert(descriptor.path_.empty()); | ||||
| } | ||||
|  | ||||
| MinidumpDescriptor& MinidumpDescriptor::operator=( | ||||
|     const MinidumpDescriptor& descriptor) { | ||||
|   assert(descriptor.path_.empty()); | ||||
|  | ||||
|   fd_ = descriptor.fd_; | ||||
|   directory_ = descriptor.directory_; | ||||
|   path_.clear(); | ||||
|   if (c_path_) { | ||||
|     // This descriptor already had a path set, so generate a new one. | ||||
|     c_path_ = NULL; | ||||
|     UpdatePath(); | ||||
|   } | ||||
|   size_limit_ = descriptor.size_limit_; | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| void MinidumpDescriptor::UpdatePath() { | ||||
|   assert(fd_ == -1 && !directory_.empty()); | ||||
|  | ||||
|   GUID guid; | ||||
|   char guid_str[kGUIDStringLength + 1]; | ||||
|   if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) { | ||||
|     assert(false); | ||||
|   } | ||||
|  | ||||
|   path_.clear(); | ||||
|   path_ = directory_ + "/" + guid_str + ".dmp";   | ||||
|   c_path_ = path_.c_str(); | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										100
									
								
								sdk/breakpad/client/linux/handler/minidump_descriptor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								sdk/breakpad/client/linux/handler/minidump_descriptor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| // Copyright (c) 2012 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ | ||||
| #define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| // The MinidumpDescriptor describes how to access a minidump: it can contain | ||||
| // either a file descriptor or a path. | ||||
| // Note that when using files, it is created with the path to a directory. | ||||
| // The actual path where the minidump is generated is created by this class. | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class MinidumpDescriptor { | ||||
|  public: | ||||
|   MinidumpDescriptor() : fd_(-1), size_limit_(-1) {} | ||||
|  | ||||
|   explicit MinidumpDescriptor(const string& directory) | ||||
|       : fd_(-1), | ||||
|         directory_(directory), | ||||
|         c_path_(NULL), | ||||
|         size_limit_(-1) { | ||||
|     assert(!directory.empty()); | ||||
|   } | ||||
|  | ||||
|   explicit MinidumpDescriptor(int fd) | ||||
|       : fd_(fd), | ||||
|         c_path_(NULL), | ||||
|         size_limit_(-1) { | ||||
|     assert(fd != -1); | ||||
|   } | ||||
|  | ||||
|   explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor); | ||||
|   MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor); | ||||
|  | ||||
|   bool IsFD() const { return fd_ != -1; } | ||||
|  | ||||
|   int fd() const { return fd_; } | ||||
|  | ||||
|   string directory() const { return directory_; } | ||||
|  | ||||
|   const char* path() const { return c_path_; } | ||||
|  | ||||
|   // Updates the path so it is unique. | ||||
|   // Should be called from a normal context: this methods uses the heap. | ||||
|   void UpdatePath(); | ||||
|  | ||||
|   off_t size_limit() const { return size_limit_; } | ||||
|   void set_size_limit(off_t limit) { size_limit_ = limit; } | ||||
|  | ||||
|  private: | ||||
|   // The file descriptor where the minidump is generated. | ||||
|   int fd_; | ||||
|  | ||||
|   // The directory where the minidump should be generated. | ||||
|   string directory_; | ||||
|   // The full path to the generated minidump. | ||||
|   string path_; | ||||
|   // The C string of |path_|. Precomputed so it can be access from a compromised | ||||
|   // context. | ||||
|   const char* c_path_; | ||||
|  | ||||
|   off_t size_limit_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ | ||||
							
								
								
									
										
											BIN
										
									
								
								sdk/breakpad/client/linux/linux_dumper_unittest_helper
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								sdk/breakpad/client/linux/linux_dumper_unittest_helper
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										48
									
								
								sdk/breakpad/client/linux/log/log.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								sdk/breakpad/client/linux/log/log.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| // Copyright (c) 2012 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include "client/linux/log/log.h" | ||||
|  | ||||
| #if defined(__ANDROID__) | ||||
| #include <android/log.h> | ||||
| #else | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
| #endif | ||||
|  | ||||
| namespace logger { | ||||
|  | ||||
| int write(const char* buf, size_t nbytes) { | ||||
| #if defined(__ANDROID__) | ||||
|   return __android_log_write(ANDROID_LOG_WARN, "google-breakpad", buf); | ||||
| #else | ||||
|   return sys_write(2, buf, nbytes); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| }  // namespace logger | ||||
							
								
								
									
										41
									
								
								sdk/breakpad/client/linux/log/log.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								sdk/breakpad/client/linux/log/log.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_LOG_LOG_H_ | ||||
| #define CLIENT_LINUX_LOG_LOG_H_ | ||||
|  | ||||
| #include <stddef.h> | ||||
|  | ||||
| namespace logger { | ||||
|  | ||||
| int write(const char* buf, size_t nbytes); | ||||
|  | ||||
| }  // namespace logger | ||||
|  | ||||
| #endif  // CLIENT_LINUX_LOG_LOG_H_ | ||||
							
								
								
									
										144
									
								
								sdk/breakpad/client/linux/minidump_writer/cpu_set.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								sdk/breakpad/client/linux/minidump_writer/cpu_set.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | ||||
| // Copyright (c) 2013, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // Helper class used to model a set of CPUs, as read from sysfs | ||||
| // files like /sys/devices/system/cpu/present | ||||
| // See See http://www.kernel.org/doc/Documentation/cputopology.txt | ||||
| class CpuSet { | ||||
| public: | ||||
|   // The maximum number of supported CPUs. | ||||
|   static const size_t kMaxCpus = 1024; | ||||
|  | ||||
|   CpuSet() { | ||||
|     my_memset(mask_, 0, sizeof(mask_)); | ||||
|   } | ||||
|  | ||||
|   // Parse a sysfs file to extract the corresponding CPU set. | ||||
|   bool ParseSysFile(int fd) { | ||||
|     char buffer[512]; | ||||
|     int ret = sys_read(fd, buffer, sizeof(buffer)-1); | ||||
|     if (ret < 0) | ||||
|       return false; | ||||
|  | ||||
|     buffer[ret] = '\0'; | ||||
|  | ||||
|     // Expected format: comma-separated list of items, where each | ||||
|     // item can be a decimal integer, or two decimal integers separated | ||||
|     // by a dash. | ||||
|     // E.g.: | ||||
|     //       0 | ||||
|     //       0,1,2,3 | ||||
|     //       0-3 | ||||
|     //       1,10-23 | ||||
|     const char* p = buffer; | ||||
|     const char* p_end = p + ret; | ||||
|     while (p < p_end) { | ||||
|       // Skip leading space, if any | ||||
|       while (p < p_end && my_isspace(*p)) | ||||
|         p++; | ||||
|  | ||||
|       // Find start and size of current item. | ||||
|       const char* item = p; | ||||
|       size_t item_len = static_cast<size_t>(p_end - p); | ||||
|       const char* item_next = | ||||
|           static_cast<const char*>(my_memchr(p, ',', item_len)); | ||||
|       if (item_next != NULL) { | ||||
|         p = item_next + 1; | ||||
|         item_len = static_cast<size_t>(item_next - item); | ||||
|       } else { | ||||
|         p = p_end; | ||||
|         item_next = p_end; | ||||
|       } | ||||
|  | ||||
|       // Ignore trailing spaces. | ||||
|       while (item_next > item && my_isspace(item_next[-1])) | ||||
|         item_next--; | ||||
|  | ||||
|       // skip empty items. | ||||
|       if (item_next == item) | ||||
|         continue; | ||||
|  | ||||
|       // read first decimal value. | ||||
|       uintptr_t start = 0; | ||||
|       const char* next = my_read_decimal_ptr(&start, item); | ||||
|       uintptr_t end = start; | ||||
|       if (*next == '-') | ||||
|         my_read_decimal_ptr(&end, next+1); | ||||
|  | ||||
|       while (start <= end) | ||||
|         SetBit(start++); | ||||
|     } | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   // Intersect this CPU set with another one. | ||||
|   void IntersectWith(const CpuSet& other) { | ||||
|     for (size_t nn = 0; nn < kMaskWordCount; ++nn) | ||||
|       mask_[nn] &= other.mask_[nn]; | ||||
|   } | ||||
|  | ||||
|   // Return the number of CPUs in this set. | ||||
|   int GetCount() { | ||||
|     int result = 0; | ||||
|     for (size_t nn = 0; nn < kMaskWordCount; ++nn) { | ||||
|       result += __builtin_popcount(mask_[nn]); | ||||
|     } | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
| private: | ||||
|   void SetBit(uintptr_t index) { | ||||
|     size_t nn = static_cast<size_t>(index); | ||||
|     if (nn < kMaxCpus) | ||||
|       mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits)); | ||||
|   } | ||||
|  | ||||
|   typedef uint32_t MaskWordType; | ||||
|   static const size_t kMaskWordBits = 8*sizeof(MaskWordType); | ||||
|   static const size_t kMaskWordCount = | ||||
|       (kMaxCpus + kMaskWordBits - 1) / kMaskWordBits; | ||||
|  | ||||
|   MaskWordType mask_[kMaskWordCount]; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ | ||||
							
								
								
									
										164
									
								
								sdk/breakpad/client/linux/minidump_writer/cpu_set_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								sdk/breakpad/client/linux/minidump_writer/cpu_set_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| // Copyright (c) 2013, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "client/linux/minidump_writer/cpu_set.h" | ||||
| #include "common/linux/tests/auto_testfile.h" | ||||
|  | ||||
| using namespace google_breakpad; | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| typedef testing::Test CpuSetTest; | ||||
|  | ||||
| // Helper class to write test text file to a temporary file and return | ||||
| // its file descriptor. | ||||
| class ScopedTestFile : public AutoTestFile { | ||||
| public: | ||||
|   explicit ScopedTestFile(const char* text) | ||||
|     : AutoTestFile("cpu_set", text) { | ||||
|   } | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, EmptyCount) { | ||||
|   CpuSet set; | ||||
|   ASSERT_EQ(0, set.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, OneCpu) { | ||||
|   ScopedTestFile file("10"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|  | ||||
|   CpuSet set; | ||||
|   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||
|   ASSERT_EQ(1, set.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, OneCpuTerminated) { | ||||
|   ScopedTestFile file("10\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|  | ||||
|   CpuSet set; | ||||
|   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||
|   ASSERT_EQ(1, set.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, TwoCpusWithComma) { | ||||
|   ScopedTestFile file("1,10"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|  | ||||
|   CpuSet set; | ||||
|   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||
|   ASSERT_EQ(2, set.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, TwoCpusWithRange) { | ||||
|   ScopedTestFile file("1-2"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|  | ||||
|   CpuSet set; | ||||
|   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||
|   ASSERT_EQ(2, set.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, TenCpusWithRange) { | ||||
|   ScopedTestFile file("9-18"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|  | ||||
|   CpuSet set; | ||||
|   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||
|   ASSERT_EQ(10, set.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, MultiItems) { | ||||
|   ScopedTestFile file("0, 2-4, 128"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|  | ||||
|   CpuSet set; | ||||
|   ASSERT_TRUE(set.ParseSysFile(file.GetFd())); | ||||
|   ASSERT_EQ(5, set.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, IntersectWith) { | ||||
|   ScopedTestFile file1("9-19"); | ||||
|   ASSERT_TRUE(file1.IsOk()); | ||||
|   CpuSet set1; | ||||
|   ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); | ||||
|   ASSERT_EQ(11, set1.GetCount()); | ||||
|  | ||||
|   ScopedTestFile file2("16-24"); | ||||
|   ASSERT_TRUE(file2.IsOk()); | ||||
|   CpuSet set2; | ||||
|   ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); | ||||
|   ASSERT_EQ(9, set2.GetCount()); | ||||
|  | ||||
|   set1.IntersectWith(set2); | ||||
|   ASSERT_EQ(4, set1.GetCount()); | ||||
|   ASSERT_EQ(9, set2.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, SelfIntersection) { | ||||
|   ScopedTestFile file1("9-19"); | ||||
|   ASSERT_TRUE(file1.IsOk()); | ||||
|   CpuSet set1; | ||||
|   ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); | ||||
|   ASSERT_EQ(11, set1.GetCount()); | ||||
|  | ||||
|   set1.IntersectWith(set1); | ||||
|   ASSERT_EQ(11, set1.GetCount()); | ||||
| } | ||||
|  | ||||
| TEST(CpuSetTest, EmptyIntersection) { | ||||
|   ScopedTestFile file1("0-19"); | ||||
|   ASSERT_TRUE(file1.IsOk()); | ||||
|   CpuSet set1; | ||||
|   ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); | ||||
|   ASSERT_EQ(20, set1.GetCount()); | ||||
|  | ||||
|   ScopedTestFile file2("20-39"); | ||||
|   ASSERT_TRUE(file2.IsOk()); | ||||
|   CpuSet set2; | ||||
|   ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); | ||||
|   ASSERT_EQ(20, set2.GetCount()); | ||||
|  | ||||
|   set1.IntersectWith(set2); | ||||
|   ASSERT_EQ(0, set1.GetCount()); | ||||
|  | ||||
|   ASSERT_EQ(20, set2.GetCount()); | ||||
| } | ||||
|  | ||||
							
								
								
									
										106
									
								
								sdk/breakpad/client/linux/minidump_writer/directory_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								sdk/breakpad/client/linux/minidump_writer/directory_reader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <unistd.h> | ||||
| #include <limits.h> | ||||
| #include <assert.h> | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // A class for enumerating a directory without using diropen/readdir or other | ||||
| // functions which may allocate memory. | ||||
| class DirectoryReader { | ||||
|  public: | ||||
|   DirectoryReader(int fd) | ||||
|       : fd_(fd), | ||||
|         buf_used_(0) { | ||||
|   } | ||||
|  | ||||
|   // Return the next entry from the directory | ||||
|   //   name: (output) the NUL terminated entry name | ||||
|   // | ||||
|   // Returns true iff successful (false on EOF). | ||||
|   // | ||||
|   // After calling this, one must call |PopEntry| otherwise you'll get the same | ||||
|   // entry over and over. | ||||
|   bool GetNextEntry(const char** name) { | ||||
|     struct kernel_dirent* const dent = | ||||
|       reinterpret_cast<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; | ||||
|     my_memmove(buf_, buf_ + dent->d_reclen, buf_used_); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   const int fd_; | ||||
|   bool hit_eof_; | ||||
|   unsigned buf_used_; | ||||
|   uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ | ||||
| @@ -0,0 +1,78 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <set> | ||||
| #include <string> | ||||
|  | ||||
| #include <dirent.h> | ||||
| #include <fcntl.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include "client/linux/minidump_writer/directory_reader.h" | ||||
| #include "common/using_std_string.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
|  | ||||
| using namespace google_breakpad; | ||||
|  | ||||
| namespace { | ||||
| typedef testing::Test DirectoryReaderTest; | ||||
| } | ||||
|  | ||||
| TEST(DirectoryReaderTest, CompareResults) { | ||||
|   std::set<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); | ||||
| } | ||||
							
								
								
									
										131
									
								
								sdk/breakpad/client/linux/minidump_writer/line_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								sdk/breakpad/client/linux/minidump_writer/line_reader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // A class for reading a file, line by line, without using fopen/fgets or other | ||||
| // functions which may allocate memory. | ||||
| class LineReader { | ||||
|  public: | ||||
|   LineReader(int fd) | ||||
|       : fd_(fd), | ||||
|         hit_eof_(false), | ||||
|         buf_used_(0) { | ||||
|   } | ||||
|  | ||||
|   // The maximum length of a line. | ||||
|   static const size_t kMaxLineLen = 512; | ||||
|  | ||||
|   // Return the next line from the file. | ||||
|   //   line: (output) a pointer to the start of the line. The line is NUL | ||||
|   //     terminated. | ||||
|   //   len: (output) the length of the line (not inc the NUL byte) | ||||
|   // | ||||
|   // Returns true iff successful (false on EOF). | ||||
|   // | ||||
|   // One must call |PopLine| after this function, otherwise you'll continue to | ||||
|   // get the same line over and over. | ||||
|   bool GetNextLine(const char **line, unsigned *len) { | ||||
|     for (;;) { | ||||
|       if (buf_used_ == 0 && hit_eof_) | ||||
|         return false; | ||||
|  | ||||
|       for (unsigned i = 0; i < buf_used_; ++i) { | ||||
|         if (buf_[i] == '\n' || buf_[i] == 0) { | ||||
|           buf_[i] = 0; | ||||
|           *len = i; | ||||
|           *line = buf_; | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       if (buf_used_ == sizeof(buf_)) { | ||||
|         // we scanned the whole buffer and didn't find an end-of-line marker. | ||||
|         // This line is too long to process. | ||||
|         return false; | ||||
|       } | ||||
|  | ||||
|       // We didn't find any end-of-line terminators in the buffer. However, if | ||||
|       // this is the last line in the file it might not have one: | ||||
|       if (hit_eof_) { | ||||
|         assert(buf_used_); | ||||
|         // There's room for the NUL because of the buf_used_ == sizeof(buf_) | ||||
|         // check above. | ||||
|         buf_[buf_used_] = 0; | ||||
|         *len = buf_used_; | ||||
|         buf_used_ += 1;  // since we appended the NUL. | ||||
|         *line = buf_; | ||||
|         return true; | ||||
|       } | ||||
|  | ||||
|       // Otherwise, we should pull in more data from the file | ||||
|       const ssize_t n = sys_read(fd_, buf_ + buf_used_, | ||||
|                                  sizeof(buf_) - buf_used_); | ||||
|       if (n < 0) { | ||||
|         return false; | ||||
|       } else if (n == 0) { | ||||
|         hit_eof_ = true; | ||||
|       } else { | ||||
|         buf_used_ += n; | ||||
|       } | ||||
|  | ||||
|       // At this point, we have either set the hit_eof_ flag, or we have more | ||||
|       // data to process... | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void PopLine(unsigned len) { | ||||
|     // len doesn't include the NUL byte at the end. | ||||
|  | ||||
|     assert(buf_used_ >= len + 1); | ||||
|     buf_used_ -= len + 1; | ||||
|     my_memmove(buf_, buf_ + len + 1, buf_used_); | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   const int fd_; | ||||
|  | ||||
|   bool hit_eof_; | ||||
|   unsigned buf_used_; | ||||
|   char buf_[kMaxLineLen]; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ | ||||
| @@ -0,0 +1,169 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include "client/linux/minidump_writer/line_reader.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/linux/tests/auto_testfile.h" | ||||
|  | ||||
| using namespace google_breakpad; | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| typedef testing::Test LineReaderTest; | ||||
|  | ||||
| class ScopedTestFile : public AutoTestFile { | ||||
| public: | ||||
|   explicit ScopedTestFile(const char* text) | ||||
|     : AutoTestFile("line_reader", text) { | ||||
|   } | ||||
|  | ||||
|   ScopedTestFile(const char* text, size_t text_len) | ||||
|     : AutoTestFile("line_reader", text, text_len) { | ||||
|   } | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| TEST(LineReaderTest, EmptyFile) { | ||||
|   ScopedTestFile file(""); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   LineReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *line; | ||||
|   unsigned len; | ||||
|   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||
| } | ||||
|  | ||||
| TEST(LineReaderTest, OneLineTerminated) { | ||||
|   ScopedTestFile file("a\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   LineReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *line; | ||||
|   unsigned int len; | ||||
|   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||
|   ASSERT_EQ((unsigned int)1, len); | ||||
|   ASSERT_EQ('a', line[0]); | ||||
|   ASSERT_EQ('\0', line[1]); | ||||
|   reader.PopLine(len); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||
| } | ||||
|  | ||||
| TEST(LineReaderTest, OneLine) { | ||||
|   ScopedTestFile file("a"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   LineReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *line; | ||||
|   unsigned len; | ||||
|   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||
|   ASSERT_EQ((unsigned)1, len); | ||||
|   ASSERT_EQ('a', line[0]); | ||||
|   ASSERT_EQ('\0', line[1]); | ||||
|   reader.PopLine(len); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||
| } | ||||
|  | ||||
| TEST(LineReaderTest, TwoLinesTerminated) { | ||||
|   ScopedTestFile file("a\nb\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   LineReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *line; | ||||
|   unsigned len; | ||||
|   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||
|   ASSERT_EQ((unsigned)1, len); | ||||
|   ASSERT_EQ('a', line[0]); | ||||
|   ASSERT_EQ('\0', line[1]); | ||||
|   reader.PopLine(len); | ||||
|  | ||||
|   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||
|   ASSERT_EQ((unsigned)1, len); | ||||
|   ASSERT_EQ('b', line[0]); | ||||
|   ASSERT_EQ('\0', line[1]); | ||||
|   reader.PopLine(len); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||
| } | ||||
|  | ||||
| TEST(LineReaderTest, TwoLines) { | ||||
|   ScopedTestFile file("a\nb"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   LineReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *line; | ||||
|   unsigned len; | ||||
|   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||
|   ASSERT_EQ((unsigned)1, len); | ||||
|   ASSERT_EQ('a', line[0]); | ||||
|   ASSERT_EQ('\0', line[1]); | ||||
|   reader.PopLine(len); | ||||
|  | ||||
|   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||
|   ASSERT_EQ((unsigned)1, len); | ||||
|   ASSERT_EQ('b', line[0]); | ||||
|   ASSERT_EQ('\0', line[1]); | ||||
|   reader.PopLine(len); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||
| } | ||||
|  | ||||
| TEST(LineReaderTest, MaxLength) { | ||||
|   char l[LineReader::kMaxLineLen-1]; | ||||
|   memset(l, 'a', sizeof(l)); | ||||
|   ScopedTestFile file(l, sizeof(l)); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   LineReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *line; | ||||
|   unsigned len; | ||||
|   ASSERT_TRUE(reader.GetNextLine(&line, &len)); | ||||
|   ASSERT_EQ(sizeof(l), len); | ||||
|   ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); | ||||
|   ASSERT_EQ('\0', line[len]); | ||||
| } | ||||
|  | ||||
| TEST(LineReaderTest, TooLong) { | ||||
|   // Note: this writes kMaxLineLen 'a' chars in the test file. | ||||
|   char l[LineReader::kMaxLineLen]; | ||||
|   memset(l, 'a', sizeof(l)); | ||||
|   ScopedTestFile file(l, sizeof(l)); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   LineReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *line; | ||||
|   unsigned len; | ||||
|   ASSERT_FALSE(reader.GetNextLine(&line, &len)); | ||||
| } | ||||
							
								
								
									
										236
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_core_dumper.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_core_dumper.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,236 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper. | ||||
| // See linux_core_dumper.h for details. | ||||
|  | ||||
| #include "client/linux/minidump_writer/linux_core_dumper.h" | ||||
|  | ||||
| #include <asm/ptrace.h> | ||||
| #include <assert.h> | ||||
| #include <elf.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
| #include <sys/procfs.h> | ||||
|  | ||||
| #include "common/linux/linux_libc_support.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| LinuxCoreDumper::LinuxCoreDumper(pid_t pid, | ||||
|                                  const char* core_path, | ||||
|                                  const char* procfs_path) | ||||
|     : LinuxDumper(pid), | ||||
|       core_path_(core_path), | ||||
|       procfs_path_(procfs_path), | ||||
|       thread_infos_(&allocator_, 8) { | ||||
|   assert(core_path_); | ||||
| } | ||||
|  | ||||
| bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid, | ||||
|                                     const char* node) const { | ||||
|   if (!path || !node) | ||||
|     return false; | ||||
|  | ||||
|   size_t node_len = my_strlen(node); | ||||
|   if (node_len == 0) | ||||
|     return false; | ||||
|  | ||||
|   size_t procfs_path_len = my_strlen(procfs_path_); | ||||
|   size_t total_length = procfs_path_len + 1 + node_len; | ||||
|   if (total_length >= NAME_MAX) | ||||
|     return false; | ||||
|  | ||||
|   memcpy(path, procfs_path_, procfs_path_len); | ||||
|   path[procfs_path_len] = '/'; | ||||
|   memcpy(path + procfs_path_len + 1, node, node_len); | ||||
|   path[total_length] = '\0'; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, | ||||
|                                       const void* src, size_t length) { | ||||
|   ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src); | ||||
|   // TODO(benchan): Investigate whether the data to be copied could span | ||||
|   // across multiple segments in the core dump file. ElfCoreDump::CopyData | ||||
|   // and this method do not handle that case yet. | ||||
|   if (!core_.CopyData(dest, virtual_address, length)) { | ||||
|     // If the data segment is not found in the core dump, fill the result | ||||
|     // with marker characters. | ||||
|     memset(dest, 0xab, length); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { | ||||
|   if (index >= thread_infos_.size()) | ||||
|     return false; | ||||
|  | ||||
|   *info = thread_infos_[index]; | ||||
|   const uint8_t* stack_pointer; | ||||
| #if defined(__i386) | ||||
|   memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); | ||||
| #elif defined(__x86_64) | ||||
|   memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); | ||||
| #elif defined(__ARM_EABI__) | ||||
|   memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); | ||||
| #elif defined(__mips__) | ||||
|   stack_pointer =  | ||||
|       reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]); | ||||
| #else | ||||
| #error "This code hasn't been ported to your platform yet." | ||||
| #endif | ||||
|   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool LinuxCoreDumper::IsPostMortem() const { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool LinuxCoreDumper::ThreadsSuspend() { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool LinuxCoreDumper::ThreadsResume() { | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool LinuxCoreDumper::EnumerateThreads() { | ||||
|   if (!mapped_core_file_.Map(core_path_)) { | ||||
|     fprintf(stderr, "Could not map core dump file into memory\n"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   core_.SetContent(mapped_core_file_.content()); | ||||
|   if (!core_.IsValid()) { | ||||
|     fprintf(stderr, "Invalid core dump file\n"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   ElfCoreDump::Note note = core_.GetFirstNote(); | ||||
|   if (!note.IsValid()) { | ||||
|     fprintf(stderr, "PT_NOTE section not found\n"); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   bool first_thread = true; | ||||
|   do { | ||||
|     ElfCoreDump::Word type = note.GetType(); | ||||
|     MemoryRange name = note.GetName(); | ||||
|     MemoryRange description = note.GetDescription(); | ||||
|  | ||||
|     if (type == 0 || name.IsEmpty() || description.IsEmpty()) { | ||||
|       fprintf(stderr, "Could not found a valid PT_NOTE.\n"); | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are | ||||
|     // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): | ||||
|     //   Thread           Name          Type | ||||
|     //   ------------------------------------------------------------------- | ||||
|     //   1st thread       CORE          NT_PRSTATUS | ||||
|     //   process-wide     CORE          NT_PRPSINFO | ||||
|     //   process-wide     CORE          NT_AUXV | ||||
|     //   1st thread       CORE          NT_FPREGSET | ||||
|     //   1st thread       LINUX         NT_PRXFPREG | ||||
|     //   1st thread       LINUX         NT_386_TLS | ||||
|     // | ||||
|     //   2nd thread       CORE          NT_PRSTATUS | ||||
|     //   2nd thread       CORE          NT_FPREGSET | ||||
|     //   2nd thread       LINUX         NT_PRXFPREG | ||||
|     //   2nd thread       LINUX         NT_386_TLS | ||||
|     // | ||||
|     //   3rd thread       CORE          NT_PRSTATUS | ||||
|     //   3rd thread       CORE          NT_FPREGSET | ||||
|     //   3rd thread       LINUX         NT_PRXFPREG | ||||
|     //   3rd thread       LINUX         NT_386_TLS | ||||
|     // | ||||
|     // The following code only works if notes are ordered as expected. | ||||
|     switch (type) { | ||||
|       case NT_PRSTATUS: { | ||||
|         if (description.length() != sizeof(elf_prstatus)) { | ||||
|           fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n"); | ||||
|           return false; | ||||
|         } | ||||
|  | ||||
|         const elf_prstatus* status = | ||||
|             reinterpret_cast<const elf_prstatus*>(description.data()); | ||||
|         pid_t pid = status->pr_pid; | ||||
|         ThreadInfo info; | ||||
|         memset(&info, 0, sizeof(ThreadInfo)); | ||||
|         info.tgid = status->pr_pgrp; | ||||
|         info.ppid = status->pr_ppid; | ||||
|         memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); | ||||
|         if (first_thread) { | ||||
|           crash_thread_ = pid; | ||||
|           crash_signal_ = status->pr_info.si_signo; | ||||
|         } | ||||
|         first_thread = false; | ||||
|         threads_.push_back(pid); | ||||
|         thread_infos_.push_back(info); | ||||
|         break; | ||||
|       } | ||||
| #if defined(__i386) || defined(__x86_64) | ||||
|       case NT_FPREGSET: { | ||||
|         if (thread_infos_.empty()) | ||||
|           return false; | ||||
|  | ||||
|         ThreadInfo* info = &thread_infos_.back(); | ||||
|         if (description.length() != sizeof(info->fpregs)) { | ||||
|           fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n"); | ||||
|           return false; | ||||
|         } | ||||
|  | ||||
|         memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); | ||||
|         break; | ||||
|       } | ||||
| #endif | ||||
| #if defined(__i386) | ||||
|       case NT_PRXFPREG: { | ||||
|         if (thread_infos_.empty()) | ||||
|           return false; | ||||
|  | ||||
|         ThreadInfo* info = &thread_infos_.back(); | ||||
|         if (description.length() != sizeof(info->fpxregs)) { | ||||
|           fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n"); | ||||
|           return false; | ||||
|         } | ||||
|  | ||||
|         memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); | ||||
|         break; | ||||
|       } | ||||
| #endif | ||||
|     } | ||||
|     note = note.GetNextNote(); | ||||
|   } while (note.IsValid()); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										122
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_core_dumper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_core_dumper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper | ||||
| // class, which is derived from google_breakpad::LinuxDumper to extract | ||||
| // information from a crashed process via its core dump and proc files. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ | ||||
|  | ||||
| #include "client/linux/minidump_writer/linux_dumper.h" | ||||
| #include "common/linux/elf_core_dump.h" | ||||
| #include "common/linux/memory_mapped_file.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class LinuxCoreDumper : public LinuxDumper { | ||||
|  public: | ||||
|   // Constructs a dumper for extracting information of a given process | ||||
|   // with a process ID of |pid| via its core dump file at |core_path| and | ||||
|   // its proc files at |procfs_path|. If |procfs_path| is a copy of | ||||
|   // /proc/<pid>, it should contain the following files: | ||||
|   //     auxv, cmdline, environ, exe, maps, status | ||||
|   LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path); | ||||
|  | ||||
|   // Implements LinuxDumper::BuildProcPath(). | ||||
|   // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>). | ||||
|   // |path| is a character array of at least NAME_MAX bytes to return the | ||||
|   // result.|node| is the final node without any slashes. Return true on | ||||
|   // success. | ||||
|   // | ||||
|   // As this dumper performs a post-mortem dump and makes use of a copy | ||||
|   // of the proc files of the crashed process, this derived method does | ||||
|   // not actually make use of |pid| and always returns a subpath of | ||||
|   // |procfs_path_| regardless of whether |pid| corresponds to the main | ||||
|   // process or a thread of the process, i.e. assuming both the main process | ||||
|   // and its threads have the following proc files with the same content: | ||||
|   //     auxv, cmdline, environ, exe, maps, status | ||||
|   virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; | ||||
|  | ||||
|   // Implements LinuxDumper::CopyFromProcess(). | ||||
|   // Copies content of |length| bytes from a given process |child|, | ||||
|   // starting from |src|, into |dest|. This method extracts the content | ||||
|   // the core dump and fills |dest| with a sequence of marker bytes | ||||
|   // if the expected data is not found in the core dump. | ||||
|   virtual void CopyFromProcess(void* dest, pid_t child, const void* src, | ||||
|                                size_t length); | ||||
|  | ||||
|   // Implements LinuxDumper::GetThreadInfoByIndex(). | ||||
|   // Reads information about the |index|-th thread of |threads_|. | ||||
|   // Returns true on success. One must have called |ThreadsSuspend| first. | ||||
|   virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); | ||||
|  | ||||
|   // Implements LinuxDumper::IsPostMortem(). | ||||
|   // Always returns true to indicate that this dumper performs a | ||||
|   // post-mortem dump of a crashed process via a core dump file. | ||||
|   virtual bool IsPostMortem() const; | ||||
|  | ||||
|   // Implements LinuxDumper::ThreadsSuspend(). | ||||
|   // As the dumper performs a post-mortem dump via a core dump file, | ||||
|   // there is no threads to suspend. This method does nothing and | ||||
|   // always returns true. | ||||
|   virtual bool ThreadsSuspend(); | ||||
|  | ||||
|   // Implements LinuxDumper::ThreadsResume(). | ||||
|   // As the dumper performs a post-mortem dump via a core dump file, | ||||
|   // there is no threads to resume. This method does nothing and | ||||
|   // always returns true. | ||||
|   virtual bool ThreadsResume(); | ||||
|  | ||||
|  protected: | ||||
|   // Implements LinuxDumper::EnumerateThreads(). | ||||
|   // Enumerates all threads of the given process into |threads_|. | ||||
|   virtual bool EnumerateThreads(); | ||||
|  | ||||
|  private: | ||||
|   // Path of the core dump file. | ||||
|   const char* core_path_; | ||||
|  | ||||
|   // Path of the directory containing the proc files of the given process, | ||||
|   // which is usually a copy of /proc/<pid>. | ||||
|   const char* procfs_path_; | ||||
|  | ||||
|   // Memory-mapped core dump file at |core_path_|. | ||||
|   MemoryMappedFile mapped_core_file_; | ||||
|  | ||||
|   // Content of the core dump file. | ||||
|   ElfCoreDump core_; | ||||
|  | ||||
|   // Thread info found in the core dump file. | ||||
|   wasteful_vector<ThreadInfo> thread_infos_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_ | ||||
| @@ -0,0 +1,113 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_core_dumper_unittest.cc: | ||||
| // Unit tests for google_breakpad::LinuxCoreDumoer. | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "client/linux/minidump_writer/linux_core_dumper.h" | ||||
| #include "common/linux/tests/crash_generator.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using namespace google_breakpad; | ||||
|  | ||||
| TEST(LinuxCoreDumperTest, BuildProcPath) { | ||||
|   const pid_t pid = getpid(); | ||||
|   const char procfs_path[] = "/procfs_copy"; | ||||
|   LinuxCoreDumper dumper(getpid(), "core_file", procfs_path); | ||||
|  | ||||
|   char maps_path[NAME_MAX] = ""; | ||||
|   char maps_path_expected[NAME_MAX]; | ||||
|   snprintf(maps_path_expected, sizeof(maps_path_expected), | ||||
|            "%s/maps", procfs_path); | ||||
|   EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); | ||||
|   EXPECT_STREQ(maps_path_expected, maps_path); | ||||
|  | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); | ||||
|  | ||||
|   char long_node[NAME_MAX]; | ||||
|   size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1; | ||||
|   memset(long_node, 'a', long_node_len); | ||||
|   long_node[long_node_len] = '\0'; | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node)); | ||||
| } | ||||
|  | ||||
| TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { | ||||
|   CrashGenerator crash_generator; | ||||
|   if (!crash_generator.HasDefaultCorePattern()) { | ||||
|     fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " | ||||
|             "is skipped due to non-default core pattern\n"); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const unsigned kNumOfThreads = 3; | ||||
|   const unsigned kCrashThread = 1; | ||||
|   const int kCrashSignal = SIGABRT; | ||||
|   pid_t child_pid; | ||||
|   // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in | ||||
|   // CrashGenerator is identified and fixed. | ||||
|   if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, | ||||
|                                         kCrashSignal, &child_pid)) { | ||||
|     fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " | ||||
|             "is skipped due to no core dump generated\n"); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const string core_file = crash_generator.GetCoreFilePath(); | ||||
|   const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); | ||||
|   LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); | ||||
|   EXPECT_TRUE(dumper.Init()); | ||||
|  | ||||
|   EXPECT_TRUE(dumper.IsPostMortem()); | ||||
|  | ||||
|   // These are no-ops and should always return true. | ||||
|   EXPECT_TRUE(dumper.ThreadsSuspend()); | ||||
|   EXPECT_TRUE(dumper.ThreadsResume()); | ||||
|  | ||||
|   // LinuxCoreDumper cannot determine the crash address and thus it always | ||||
|   // sets the crash address to 0. | ||||
|   EXPECT_EQ(0U, dumper.crash_address()); | ||||
|   EXPECT_EQ(kCrashSignal, dumper.crash_signal()); | ||||
|   EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), | ||||
|             dumper.crash_thread()); | ||||
|  | ||||
|   EXPECT_EQ(kNumOfThreads, dumper.threads().size()); | ||||
|   for (unsigned i = 0; i < kNumOfThreads; ++i) { | ||||
|     ThreadInfo info; | ||||
|     EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info)); | ||||
|     const void* stack; | ||||
|     size_t stack_len; | ||||
|     EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer)); | ||||
|     EXPECT_EQ(getpid(), info.ppid); | ||||
|   } | ||||
| } | ||||
							
								
								
									
										338
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_dumper.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										338
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_dumper.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,338 @@ | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_dumper.cc: Implement google_breakpad::LinuxDumper. | ||||
| // See linux_dumper.h for details. | ||||
|  | ||||
| // This code deals with the mechanics of getting information about a crashed | ||||
| // process. Since this code may run in a compromised address space, the same | ||||
| // rules apply as detailed at the top of minidump_writer.h: no libc calls and | ||||
| // use the alternative allocator. | ||||
|  | ||||
| #include "client/linux/minidump_writer/linux_dumper.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
| #include <stddef.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "client/linux/minidump_writer/line_reader.h" | ||||
| #include "common/linux/file_id.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/linux/memory_mapped_file.h" | ||||
| #include "common/linux/safe_readlink.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| static const char kMappedFileUnsafePrefix[] = "/dev/"; | ||||
| static const char kDeletedSuffix[] = " (deleted)"; | ||||
|  | ||||
| inline static bool IsMappedFileOpenUnsafe( | ||||
|     const google_breakpad::MappingInfo& mapping) { | ||||
|   // It is unsafe to attempt to open a mapped file that lives under /dev, | ||||
|   // because the semantics of the open may be driver-specific so we'd risk | ||||
|   // hanging the crash dumper. And a file in /dev/ almost certainly has no | ||||
|   // ELF file identifier anyways. | ||||
|   return my_strncmp(mapping.name, | ||||
|                     kMappedFileUnsafePrefix, | ||||
|                     sizeof(kMappedFileUnsafePrefix) - 1) == 0; | ||||
| } | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // All interesting auvx entry types are below AT_SYSINFO_EHDR | ||||
| #define AT_MAX AT_SYSINFO_EHDR | ||||
|  | ||||
| LinuxDumper::LinuxDumper(pid_t pid) | ||||
|     : pid_(pid), | ||||
|       crash_address_(0), | ||||
|       crash_signal_(0), | ||||
|       crash_thread_(pid), | ||||
|       threads_(&allocator_, 8), | ||||
|       mappings_(&allocator_), | ||||
|       auxv_(&allocator_, AT_MAX + 1) { | ||||
|   // The passed-in size to the constructor (above) is only a hint. | ||||
|   // Must call .resize() to do actual initialization of the elements. | ||||
|   auxv_.resize(AT_MAX + 1); | ||||
| } | ||||
|  | ||||
| LinuxDumper::~LinuxDumper() { | ||||
| } | ||||
|  | ||||
| bool LinuxDumper::Init() { | ||||
|   return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); | ||||
| } | ||||
|  | ||||
| bool | ||||
| LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, | ||||
|                                          bool member, | ||||
|                                          unsigned int mapping_id, | ||||
|                                          uint8_t identifier[sizeof(MDGUID)]) { | ||||
|   assert(!member || mapping_id < mappings_.size()); | ||||
|   my_memset(identifier, 0, sizeof(MDGUID)); | ||||
|   if (IsMappedFileOpenUnsafe(mapping)) | ||||
|     return false; | ||||
|  | ||||
|   // Special-case linux-gate because it's not a real file. | ||||
|   if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) { | ||||
|     void* linux_gate = NULL; | ||||
|     if (pid_ == sys_getpid()) { | ||||
|       linux_gate = reinterpret_cast<void*>(mapping.start_addr); | ||||
|     } else { | ||||
|       linux_gate = allocator_.Alloc(mapping.size); | ||||
|       CopyFromProcess(linux_gate, pid_, | ||||
|                       reinterpret_cast<const void*>(mapping.start_addr), | ||||
|                       mapping.size); | ||||
|     } | ||||
|     return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); | ||||
|   } | ||||
|  | ||||
|   char filename[NAME_MAX]; | ||||
|   size_t filename_len = my_strlen(mapping.name); | ||||
|   assert(filename_len < NAME_MAX); | ||||
|   if (filename_len >= NAME_MAX) | ||||
|     return false; | ||||
|   my_memcpy(filename, mapping.name, filename_len); | ||||
|   filename[filename_len] = '\0'; | ||||
|   bool filename_modified = HandleDeletedFileInMapping(filename); | ||||
|  | ||||
|   MemoryMappedFile mapped_file(filename); | ||||
|   if (!mapped_file.data())  // Should probably check if size >= ElfW(Ehdr)? | ||||
|     return false; | ||||
|  | ||||
|   bool success = | ||||
|       FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); | ||||
|   if (success && member && filename_modified) { | ||||
|     mappings_[mapping_id]->name[filename_len - | ||||
|                                 sizeof(kDeletedSuffix) + 1] = '\0'; | ||||
|   } | ||||
|  | ||||
|   return success; | ||||
| } | ||||
|  | ||||
| bool LinuxDumper::ReadAuxv() { | ||||
|   char auxv_path[NAME_MAX]; | ||||
|   if (!BuildProcPath(auxv_path, pid_, "auxv")) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   int fd = sys_open(auxv_path, O_RDONLY, 0); | ||||
|   if (fd < 0) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   elf_aux_entry one_aux_entry; | ||||
|   bool res = false; | ||||
|   while (sys_read(fd, | ||||
|                   &one_aux_entry, | ||||
|                   sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && | ||||
|          one_aux_entry.a_type != AT_NULL) { | ||||
|     if (one_aux_entry.a_type <= AT_MAX) { | ||||
|       auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val; | ||||
|       res = true; | ||||
|     } | ||||
|   } | ||||
|   sys_close(fd); | ||||
|   return res; | ||||
| } | ||||
|  | ||||
| bool LinuxDumper::EnumerateMappings() { | ||||
|   char maps_path[NAME_MAX]; | ||||
|   if (!BuildProcPath(maps_path, pid_, "maps")) | ||||
|     return false; | ||||
|  | ||||
|   // linux_gate_loc is the beginning of the kernel's mapping of | ||||
|   // linux-gate.so in the process.  It doesn't actually show up in the | ||||
|   // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR | ||||
|   // aux vector entry, which gives the information necessary to special | ||||
|   // case its entry when creating the list of mappings. | ||||
|   // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more | ||||
|   // information. | ||||
|   const void* linux_gate_loc = | ||||
|       reinterpret_cast<void *>(auxv_[AT_SYSINFO_EHDR]); | ||||
|   // Although the initial executable is usually the first mapping, it's not | ||||
|   // guaranteed (see http://crosbug.com/25355); therefore, try to use the | ||||
|   // actual entry point to find the mapping. | ||||
|   const void* entry_point_loc = reinterpret_cast<void *>(auxv_[AT_ENTRY]); | ||||
|  | ||||
|   const int fd = sys_open(maps_path, O_RDONLY, 0); | ||||
|   if (fd < 0) | ||||
|     return false; | ||||
|   LineReader* const line_reader = new(allocator_) LineReader(fd); | ||||
|  | ||||
|   const char* line; | ||||
|   unsigned line_len; | ||||
|   while (line_reader->GetNextLine(&line, &line_len)) { | ||||
|     uintptr_t start_addr, end_addr, offset; | ||||
|  | ||||
|     const char* i1 = my_read_hex_ptr(&start_addr, line); | ||||
|     if (*i1 == '-') { | ||||
|       const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); | ||||
|       if (*i2 == ' ') { | ||||
|         const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); | ||||
|         if (*i3 == ' ') { | ||||
|           const char* name = NULL; | ||||
|           // Only copy name if the name is a valid path name, or if | ||||
|           // it's the VDSO image. | ||||
|           if (((name = my_strchr(line, '/')) == NULL) && | ||||
|               linux_gate_loc && | ||||
|               reinterpret_cast<void*>(start_addr) == linux_gate_loc) { | ||||
|             name = kLinuxGateLibraryName; | ||||
|             offset = 0; | ||||
|           } | ||||
|           // Merge adjacent mappings with the same name into one module, | ||||
|           // assuming they're a single library mapped by the dynamic linker | ||||
|           if (name && !mappings_.empty()) { | ||||
|             MappingInfo* module = mappings_.back(); | ||||
|             if ((start_addr == module->start_addr + module->size) && | ||||
|                 (my_strlen(name) == my_strlen(module->name)) && | ||||
|                 (my_strncmp(name, module->name, my_strlen(name)) == 0)) { | ||||
|               module->size = end_addr - module->start_addr; | ||||
|               line_reader->PopLine(line_len); | ||||
|               continue; | ||||
|             } | ||||
|           } | ||||
|           MappingInfo* const module = new(allocator_) MappingInfo; | ||||
|           my_memset(module, 0, sizeof(MappingInfo)); | ||||
|           module->start_addr = start_addr; | ||||
|           module->size = end_addr - start_addr; | ||||
|           module->offset = offset; | ||||
|           if (name != NULL) { | ||||
|             const unsigned l = my_strlen(name); | ||||
|             if (l < sizeof(module->name)) | ||||
|               my_memcpy(module->name, name, l); | ||||
|           } | ||||
|           // If this is the entry-point mapping, and it's not already the | ||||
|           // first one, then we need to make it be first.  This is because | ||||
|           // the minidump format assumes the first module is the one that | ||||
|           // corresponds to the main executable (as codified in | ||||
|           // processor/minidump.cc:MinidumpModuleList::GetMainModule()). | ||||
|           if (entry_point_loc && | ||||
|               (entry_point_loc >= | ||||
|                   reinterpret_cast<void*>(module->start_addr)) && | ||||
|               (entry_point_loc < | ||||
|                   reinterpret_cast<void*>(module->start_addr+module->size)) && | ||||
|               !mappings_.empty()) { | ||||
|             // push the module onto the front of the list. | ||||
|             mappings_.resize(mappings_.size() + 1); | ||||
|             for (size_t idx = mappings_.size() - 1; idx > 0; idx--) | ||||
|               mappings_[idx] = mappings_[idx - 1]; | ||||
|             mappings_[0] = module; | ||||
|           } else { | ||||
|             mappings_.push_back(module); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     line_reader->PopLine(line_len); | ||||
|   } | ||||
|  | ||||
|   sys_close(fd); | ||||
|  | ||||
|   return !mappings_.empty(); | ||||
| } | ||||
|  | ||||
| // Get information about the stack, given the stack pointer. We don't try to | ||||
| // walk the stack since we might not have all the information needed to do | ||||
| // unwind. So we just grab, up to, 32k of stack. | ||||
| bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, | ||||
|                                uintptr_t int_stack_pointer) { | ||||
|   // Move the stack pointer to the bottom of the page that it's in. | ||||
|   const uintptr_t page_size = getpagesize(); | ||||
|  | ||||
|   uint8_t* const stack_pointer = | ||||
|       reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); | ||||
|  | ||||
|   // The number of bytes of stack which we try to capture. | ||||
|   static const ptrdiff_t kStackToCapture = 32 * 1024; | ||||
|  | ||||
|   const MappingInfo* mapping = FindMapping(stack_pointer); | ||||
|   if (!mapping) | ||||
|     return false; | ||||
|   const ptrdiff_t offset = stack_pointer - | ||||
|       reinterpret_cast<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; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // 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; | ||||
| } | ||||
|  | ||||
| bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { | ||||
|   static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1; | ||||
|  | ||||
|   // Check for ' (deleted)' in |path|. | ||||
|   // |path| has to be at least as long as "/x (deleted)". | ||||
|   const size_t path_len = my_strlen(path); | ||||
|   if (path_len < kDeletedSuffixLen + 2) | ||||
|     return false; | ||||
|   if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, | ||||
|                  kDeletedSuffixLen) != 0) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   // Check |path| against the /proc/pid/exe 'symlink'. | ||||
|   char exe_link[NAME_MAX]; | ||||
|   char new_path[NAME_MAX]; | ||||
|   if (!BuildProcPath(exe_link, pid_, "exe")) | ||||
|     return false; | ||||
|   if (!SafeReadLink(exe_link, new_path)) | ||||
|     return false; | ||||
|   if (my_strcmp(path, new_path) != 0) | ||||
|     return false; | ||||
|  | ||||
|   // Check to see if someone actually named their executable 'foo (deleted)'. | ||||
|   struct kernel_stat exe_stat; | ||||
|   struct kernel_stat new_path_stat; | ||||
|   if (sys_stat(exe_link, &exe_stat) == 0 && | ||||
|       sys_stat(new_path, &new_path_stat) == 0 && | ||||
|       exe_stat.st_dev == new_path_stat.st_dev && | ||||
|       exe_stat.st_ino == new_path_stat.st_ino) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   my_memcpy(path, exe_link, NAME_MAX); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										216
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_dumper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										216
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_dumper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,216 @@ | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_dumper.h: Define the google_breakpad::LinuxDumper class, which | ||||
| // is a base class for extracting information of a crashed process. It | ||||
| // was originally a complete implementation using the ptrace API, but | ||||
| // has been refactored to allow derived implementations supporting both | ||||
| // ptrace and core dump. A portion of the original implementation is now | ||||
| // in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for | ||||
| // details). | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ | ||||
|  | ||||
| #include <elf.h> | ||||
| #include <linux/limits.h> | ||||
| #include <stdint.h> | ||||
| #include <sys/types.h> | ||||
| #include <sys/user.h> | ||||
|  | ||||
| #include "common/memory.h" | ||||
| #include "google_breakpad/common/minidump_format.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| #if defined(__i386) || defined(__x86_64) | ||||
| typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; | ||||
| #endif | ||||
|  | ||||
| // Typedef for our parsing of the auxv variables in /proc/pid/auxv. | ||||
| #if defined(__i386) || defined(__ARM_EABI__) || defined(__mips__) | ||||
| typedef Elf32_auxv_t elf_aux_entry; | ||||
| #elif defined(__x86_64) | ||||
| typedef Elf64_auxv_t elf_aux_entry; | ||||
| #endif | ||||
|  | ||||
| typedef typeof(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t; | ||||
|  | ||||
| // When we find the VDSO mapping in the process's address space, this | ||||
| // is the name we use for it when writing it to the minidump. | ||||
| // This should always be less than NAME_MAX! | ||||
| const char kLinuxGateLibraryName[] = "linux-gate.so"; | ||||
|  | ||||
| // We produce one of these structures for each thread in the crashed process. | ||||
| struct ThreadInfo { | ||||
|   pid_t tgid;   // thread group id | ||||
|   pid_t ppid;   // parent process | ||||
|  | ||||
|   uintptr_t stack_pointer;  // thread stack pointer | ||||
|  | ||||
|  | ||||
| #if defined(__i386) || defined(__x86_64) | ||||
|   user_regs_struct regs; | ||||
|   user_fpregs_struct fpregs; | ||||
|   static const unsigned kNumDebugRegisters = 8; | ||||
|   debugreg_t dregs[8]; | ||||
| #if defined(__i386) | ||||
|   user_fpxregs_struct fpxregs; | ||||
| #endif  // defined(__i386) | ||||
|  | ||||
| #elif defined(__ARM_EABI__) | ||||
|   // Mimicking how strace does this(see syscall.c, search for GETREGS) | ||||
|   struct user_regs regs; | ||||
|   struct user_fpregs fpregs; | ||||
| #elif defined(__mips__) | ||||
|   user_regs_struct regs; | ||||
|   user_fpregs_struct fpregs; | ||||
|   uint32_t hi[3]; | ||||
|   uint32_t lo[3]; | ||||
|   uint32_t dsp_control; | ||||
| #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; | ||||
|   size_t offset;  // offset into the backed file. | ||||
|   char name[NAME_MAX]; | ||||
| }; | ||||
|  | ||||
| class LinuxDumper { | ||||
|  public: | ||||
|   explicit LinuxDumper(pid_t pid); | ||||
|  | ||||
|   virtual ~LinuxDumper(); | ||||
|  | ||||
|   // Parse the data for |threads| and |mappings|. | ||||
|   virtual bool Init(); | ||||
|  | ||||
|   // Return true if the dumper performs a post-mortem dump. | ||||
|   virtual bool IsPostMortem() const = 0; | ||||
|  | ||||
|   // Suspend/resume all threads in the given process. | ||||
|   virtual bool ThreadsSuspend() = 0; | ||||
|   virtual bool ThreadsResume() = 0; | ||||
|  | ||||
|   // Read information about the |index|-th thread of |threads_|. | ||||
|   // Returns true on success. One must have called |ThreadsSuspend| first. | ||||
|   virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0; | ||||
|  | ||||
|   // 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; | ||||
|   const wasteful_vector<elf_aux_val_t>& auxv() { return auxv_; } | ||||
|  | ||||
|   // Find a block of memory to take as the stack given the top of stack pointer. | ||||
|   //   stack: (output) the lowest address in the memory area | ||||
|   //   stack_len: (output) the length of the memory area | ||||
|   //   stack_top: the current top of the stack | ||||
|   bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); | ||||
|  | ||||
|   PageAllocator* allocator() { return &allocator_; } | ||||
|  | ||||
|   // Copy content of |length| bytes from a given process |child|, | ||||
|   // starting from |src|, into |dest|. | ||||
|   virtual void CopyFromProcess(void* dest, pid_t child, const void* src, | ||||
|                                size_t length) = 0; | ||||
|  | ||||
|   // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>). | ||||
|   // |path| is a character array of at least NAME_MAX bytes to return the | ||||
|   // result.|node| is the final node without any slashes. Returns true on | ||||
|   // success. | ||||
|   virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0; | ||||
|  | ||||
|   // Generate a File ID from the .text section of a mapped entry. | ||||
|   // If not a member, mapping_id is ignored. | ||||
|   bool ElfFileIdentifierForMapping(const MappingInfo& mapping, | ||||
|                                    bool member, | ||||
|                                    unsigned int mapping_id, | ||||
|                                    uint8_t identifier[sizeof(MDGUID)]); | ||||
|  | ||||
|   uintptr_t crash_address() const { return crash_address_; } | ||||
|   void set_crash_address(uintptr_t crash_address) { | ||||
|     crash_address_ = crash_address; | ||||
|   } | ||||
|  | ||||
|   int crash_signal() const { return crash_signal_; } | ||||
|   void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; } | ||||
|  | ||||
|   pid_t crash_thread() const { return crash_thread_; } | ||||
|   void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } | ||||
|  | ||||
|  protected: | ||||
|   bool ReadAuxv(); | ||||
|  | ||||
|   virtual bool EnumerateMappings(); | ||||
|  | ||||
|   virtual bool EnumerateThreads() = 0; | ||||
|  | ||||
|   // For the case where a running program has been deleted, it'll show up in | ||||
|   // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then | ||||
|   // see if '/path/to/program (deleted)' matches /proc/pid/exe and return | ||||
|   // /proc/pid/exe in |path| so ELF identifier generation works correctly. This | ||||
|   // also checks to see if '/path/to/program (deleted)' exists, so it does not | ||||
|   // get fooled by a poorly named binary. | ||||
|   // For programs that don't end with ' (deleted)', this is a no-op. | ||||
|   // This assumes |path| is a buffer with length NAME_MAX. | ||||
|   // Returns true if |path| is modified. | ||||
|   bool HandleDeletedFileInMapping(char* path) const; | ||||
|  | ||||
|    // ID of the crashed process. | ||||
|   const pid_t pid_; | ||||
|  | ||||
|   // Virtual address at which the process crashed. | ||||
|   uintptr_t crash_address_; | ||||
|  | ||||
|   // Signal that terminated the crashed process. | ||||
|   int crash_signal_; | ||||
|  | ||||
|   // ID of the crashed thread. | ||||
|   pid_t crash_thread_; | ||||
|  | ||||
|   mutable PageAllocator allocator_; | ||||
|  | ||||
|   // IDs of all the threads. | ||||
|   wasteful_vector<pid_t> threads_; | ||||
|  | ||||
|   // Info from /proc/<pid>/maps. | ||||
|   wasteful_vector<MappingInfo*> mappings_; | ||||
|  | ||||
|   // Info from /proc/<pid>/auxv | ||||
|   wasteful_vector<elf_aux_val_t> auxv_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ | ||||
| @@ -0,0 +1,92 @@ | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| // | ||||
| // Helper program for the linux_dumper class, which creates a bunch of | ||||
| // threads. The first word of each thread's stack is set to the thread | ||||
| // id. | ||||
|  | ||||
| #include <pthread.h> | ||||
| #include <stdint.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "common/scoped_ptr.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| #if defined(__ARM_EABI__) | ||||
| #define TID_PTR_REGISTER "r3" | ||||
| #elif defined(__i386) | ||||
| #define TID_PTR_REGISTER "ecx" | ||||
| #elif defined(__x86_64) | ||||
| #define TID_PTR_REGISTER "rcx" | ||||
| #elif defined(__mips__) | ||||
| #define TID_PTR_REGISTER "$1" | ||||
| #else | ||||
| #error This test has not been ported to this platform. | ||||
| #endif | ||||
|  | ||||
| void *thread_function(void *data) { | ||||
|   int pipefd = *static_cast<int *>(data); | ||||
|   volatile pid_t thread_id = syscall(__NR_gettid); | ||||
|   // Signal parent that a thread has started. | ||||
|   uint8_t byte = 1; | ||||
|   if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) { | ||||
|     perror("ERROR: parent notification failed"); | ||||
|     return NULL; | ||||
|   } | ||||
|   register volatile pid_t *thread_id_ptr asm(TID_PTR_REGISTER) = &thread_id; | ||||
|   while (true) | ||||
|     asm volatile ("" : : "r" (thread_id_ptr)); | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) { | ||||
|   if (argc < 3) { | ||||
|     fprintf(stderr, | ||||
|             "usage: linux_dumper_unittest_helper <pipe fd> <# of threads>\n"); | ||||
|     return 1; | ||||
|   } | ||||
|   int pipefd = atoi(argv[1]); | ||||
|   int num_threads = atoi(argv[2]); | ||||
|   if (num_threads < 1) { | ||||
|     fprintf(stderr, "ERROR: number of threads is 0"); | ||||
|     return 1; | ||||
|   } | ||||
|   google_breakpad::scoped_array<pthread_t> threads(new pthread_t[num_threads]); | ||||
|   pthread_attr_t thread_attributes; | ||||
|   pthread_attr_init(&thread_attributes); | ||||
|   pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED); | ||||
|   for (int i = 1; i < num_threads; i++) { | ||||
|     pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd); | ||||
|   } | ||||
|   thread_function(&pipefd); | ||||
|   return 0; | ||||
| } | ||||
							
								
								
									
										320
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										320
									
								
								sdk/breakpad/client/linux/minidump_writer/linux_ptrace_dumper.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,320 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper. | ||||
| // See linux_ptrace_dumper.h for detals. | ||||
| // This class was originally splitted from google_breakpad::LinuxDumper. | ||||
|  | ||||
| // This code deals with the mechanics of getting information about a crashed | ||||
| // process. Since this code may run in a compromised address space, the same | ||||
| // rules apply as detailed at the top of minidump_writer.h: no libc calls and | ||||
| // use the alternative allocator. | ||||
|  | ||||
| #include "client/linux/minidump_writer/linux_ptrace_dumper.h" | ||||
|  | ||||
| #include <asm/ptrace.h> | ||||
| #include <assert.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
| #include <stddef.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/ptrace.h> | ||||
| #include <sys/wait.h> | ||||
|  | ||||
| #if defined(__i386) | ||||
| #include <cpuid.h> | ||||
| #endif | ||||
|  | ||||
| #include "client/linux/minidump_writer/directory_reader.h" | ||||
| #include "client/linux/minidump_writer/line_reader.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| // Suspends a thread by attaching to it. | ||||
| static bool SuspendThread(pid_t pid) { | ||||
|   // This may fail if the thread has just died or debugged. | ||||
|   errno = 0; | ||||
|   if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && | ||||
|       errno != 0) { | ||||
|     return false; | ||||
|   } | ||||
|   while (sys_waitpid(pid, NULL, __WALL) < 0) { | ||||
|     if (errno != EINTR) { | ||||
|       sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| #if defined(__i386) || defined(__x86_64) | ||||
|   // On x86, the stack pointer is NULL or -1, when executing trusted code in | ||||
|   // the seccomp sandbox. Not only does this cause difficulties down the line | ||||
|   // when trying to dump the thread's stack, it also results in the minidumps | ||||
|   // containing information about the trusted threads. This information is | ||||
|   // generally completely meaningless and just pollutes the minidumps. | ||||
|   // We thus test the stack pointer and exclude any threads that are part of | ||||
|   // the seccomp sandbox's trusted code. | ||||
|   user_regs_struct regs; | ||||
|   if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || | ||||
| #if defined(__i386) | ||||
|       !regs.esp | ||||
| #elif defined(__x86_64) | ||||
|       !regs.rsp | ||||
| #endif | ||||
|       ) { | ||||
|     sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); | ||||
|     return false; | ||||
|   } | ||||
| #endif | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // Resumes a thread by detaching from it. | ||||
| static bool ResumeThread(pid_t pid) { | ||||
|   return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; | ||||
| } | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) | ||||
|     : LinuxDumper(pid), | ||||
|       threads_suspended_(false) { | ||||
| } | ||||
|  | ||||
| bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid, | ||||
|                                       const char* node) const { | ||||
|   if (!path || !node || pid <= 0) | ||||
|     return false; | ||||
|  | ||||
|   size_t node_len = my_strlen(node); | ||||
|   if (node_len == 0) | ||||
|     return false; | ||||
|  | ||||
|   const unsigned pid_len = my_uint_len(pid); | ||||
|   const size_t total_length = 6 + pid_len + 1 + node_len; | ||||
|   if (total_length >= NAME_MAX) | ||||
|     return false; | ||||
|  | ||||
|   my_memcpy(path, "/proc/", 6); | ||||
|   my_uitos(path + 6, pid, pid_len); | ||||
|   path[6 + pid_len] = '/'; | ||||
|   my_memcpy(path + 6 + pid_len + 1, node, node_len); | ||||
|   path[total_length] = '\0'; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, | ||||
|                                         const void* src, size_t length) { | ||||
|   unsigned long tmp = 55; | ||||
|   size_t done = 0; | ||||
|   static const size_t word_size = sizeof(tmp); | ||||
|   uint8_t* const local = (uint8_t*) dest; | ||||
|   uint8_t* const remote = (uint8_t*) src; | ||||
|  | ||||
|   while (done < length) { | ||||
|     const size_t l = (length - done > word_size) ? word_size : (length - done); | ||||
|     if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { | ||||
|       tmp = 0; | ||||
|     } | ||||
|     my_memcpy(local + done, &tmp, l); | ||||
|     done += l; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Read thread info from /proc/$pid/status. | ||||
| // Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, | ||||
| // these members are set to -1. Returns true iff all three members are | ||||
| // available. | ||||
| bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { | ||||
|   if (index >= threads_.size()) | ||||
|     return false; | ||||
|  | ||||
|   pid_t tid = threads_[index]; | ||||
|  | ||||
|   assert(info != NULL); | ||||
|   char status_path[NAME_MAX]; | ||||
|   if (!BuildProcPath(status_path, tid, "status")) | ||||
|     return false; | ||||
|  | ||||
|   const int fd = sys_open(status_path, O_RDONLY, 0); | ||||
|   if (fd < 0) | ||||
|     return false; | ||||
|  | ||||
|   LineReader* const line_reader = new(allocator_) LineReader(fd); | ||||
|   const char* line; | ||||
|   unsigned line_len; | ||||
|  | ||||
|   info->ppid = info->tgid = -1; | ||||
|  | ||||
|   while (line_reader->GetNextLine(&line, &line_len)) { | ||||
|     if (my_strncmp("Tgid:\t", line, 6) == 0) { | ||||
|       my_strtoui(&info->tgid, line + 6); | ||||
|     } else if (my_strncmp("PPid:\t", line, 6) == 0) { | ||||
|       my_strtoui(&info->ppid, line + 6); | ||||
|     } | ||||
|  | ||||
|     line_reader->PopLine(line_len); | ||||
|   } | ||||
|   sys_close(fd); | ||||
|  | ||||
|   if (info->ppid == -1 || info->tgid == -1) | ||||
|     return false; | ||||
|  | ||||
|   if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
| #if defined(__i386) | ||||
| #if !defined(bit_FXSAVE)  // e.g. Clang | ||||
| #define bit_FXSAVE bit_FXSR | ||||
| #endif | ||||
|   // Detect if the CPU supports the FXSAVE/FXRSTOR instructions | ||||
|   int eax, ebx, ecx, edx; | ||||
|   __cpuid(1, eax, ebx, ecx, edx); | ||||
|   if (edx & bit_FXSAVE) { | ||||
|     if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) { | ||||
|       return false; | ||||
|     } | ||||
|   } else { | ||||
|     memset(&info->fpxregs, 0, sizeof(info->fpxregs)); | ||||
|   } | ||||
| #endif  // defined(__i386) | ||||
|  | ||||
| #if defined(__i386) || defined(__x86_64) | ||||
|   for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { | ||||
|     if (sys_ptrace( | ||||
|         PTRACE_PEEKUSER, tid, | ||||
|         reinterpret_cast<void*> (offsetof(struct user, | ||||
|                                           u_debugreg[0]) + i * | ||||
|                                  sizeof(debugreg_t)), | ||||
|         &info->dregs[i]) == -1) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
| #endif | ||||
|  | ||||
| #if defined(__mips__) | ||||
|   for (int i = 0; i < 3; ++i) { | ||||
|     sys_ptrace(PTRACE_PEEKUSER, tid, | ||||
|                reinterpret_cast<void*>(DSP_BASE + (i * 2)), &info->hi[i]); | ||||
|     sys_ptrace(PTRACE_PEEKUSER, tid, | ||||
|                reinterpret_cast<void*>(DSP_BASE + (i * 2) + 1), &info->lo[i]); | ||||
|   } | ||||
|   sys_ptrace(PTRACE_PEEKUSER, tid, | ||||
|              reinterpret_cast<void*>(DSP_CONTROL), &info->dsp_control); | ||||
| #endif | ||||
|  | ||||
|   const uint8_t* stack_pointer; | ||||
| #if defined(__i386) | ||||
|   my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); | ||||
| #elif defined(__x86_64) | ||||
|   my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); | ||||
| #elif defined(__ARM_EABI__) | ||||
|   my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); | ||||
| #elif defined(__mips__) | ||||
|   stack_pointer = | ||||
|       reinterpret_cast<uint8_t*>(info->regs.regs[MD_CONTEXT_MIPS_REG_SP]); | ||||
| #else | ||||
| #error "This code hasn't been ported to your platform yet." | ||||
| #endif | ||||
|   info->stack_pointer = reinterpret_cast<uintptr_t>(stack_pointer); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool LinuxPtraceDumper::IsPostMortem() const { | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool LinuxPtraceDumper::ThreadsSuspend() { | ||||
|   if (threads_suspended_) | ||||
|     return true; | ||||
|   for (size_t i = 0; i < threads_.size(); ++i) { | ||||
|     if (!SuspendThread(threads_[i])) { | ||||
|       // If the thread either disappeared before we could attach to it, or if | ||||
|       // it was part of the seccomp sandbox's trusted code, it is OK to | ||||
|       // silently drop it from the minidump. | ||||
|       my_memmove(&threads_[i], &threads_[i+1], | ||||
|                  (threads_.size() - i - 1) * sizeof(threads_[i])); | ||||
|       threads_.resize(threads_.size() - 1); | ||||
|       --i; | ||||
|     } | ||||
|   } | ||||
|   threads_suspended_ = true; | ||||
|   return threads_.size() > 0; | ||||
| } | ||||
|  | ||||
| bool LinuxPtraceDumper::ThreadsResume() { | ||||
|   if (!threads_suspended_) | ||||
|     return false; | ||||
|   bool good = true; | ||||
|   for (size_t i = 0; i < threads_.size(); ++i) | ||||
|     good &= ResumeThread(threads_[i]); | ||||
|   threads_suspended_ = false; | ||||
|   return good; | ||||
| } | ||||
|  | ||||
| // Parse /proc/$pid/task to list all the threads of the process identified by | ||||
| // pid. | ||||
| bool LinuxPtraceDumper::EnumerateThreads() { | ||||
|   char task_path[NAME_MAX]; | ||||
|   if (!BuildProcPath(task_path, pid_, "task")) | ||||
|     return false; | ||||
|  | ||||
|   const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); | ||||
|   if (fd < 0) | ||||
|     return false; | ||||
|   DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); | ||||
|  | ||||
|   // The directory may contain duplicate entries which we filter by assuming | ||||
|   // that they are consecutive. | ||||
|   int last_tid = -1; | ||||
|   const char* dent_name; | ||||
|   while (dir_reader->GetNextEntry(&dent_name)) { | ||||
|     if (my_strcmp(dent_name, ".") && | ||||
|         my_strcmp(dent_name, "..")) { | ||||
|       int tid = 0; | ||||
|       if (my_strtoui(&tid, dent_name) && | ||||
|           last_tid != tid) { | ||||
|         last_tid = tid; | ||||
|         threads_.push_back(tid); | ||||
|       } | ||||
|     } | ||||
|     dir_reader->PopEntry(); | ||||
|   } | ||||
|  | ||||
|   sys_close(fd); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
| @@ -0,0 +1,92 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper | ||||
| // class, which is derived from google_breakpad::LinuxDumper to extract | ||||
| // information from a crashed process via ptrace. | ||||
| // This class was originally splitted from google_breakpad::LinuxDumper. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ | ||||
|  | ||||
| #include "client/linux/minidump_writer/linux_dumper.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class LinuxPtraceDumper : public LinuxDumper { | ||||
|  public: | ||||
|   // Constructs a dumper for extracting information of a given process | ||||
|   // with a process ID of |pid|. | ||||
|   explicit LinuxPtraceDumper(pid_t pid); | ||||
|  | ||||
|   // Implements LinuxDumper::BuildProcPath(). | ||||
|   // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>). | ||||
|   // |path| is a character array of at least NAME_MAX bytes to return the | ||||
|   // result. |node| is the final node without any slashes. Returns true on | ||||
|   // success. | ||||
|   virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; | ||||
|  | ||||
|   // Implements LinuxDumper::CopyFromProcess(). | ||||
|   // Copies content of |length| bytes from a given process |child|, | ||||
|   // starting from |src|, into |dest|. This method uses ptrace to extract | ||||
|   // the content from the target process. | ||||
|   virtual void CopyFromProcess(void* dest, pid_t child, const void* src, | ||||
|                                size_t length); | ||||
|  | ||||
|   // Implements LinuxDumper::GetThreadInfoByIndex(). | ||||
|   // Reads information about the |index|-th thread of |threads_|. | ||||
|   // Returns true on success. One must have called |ThreadsSuspend| first. | ||||
|   virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); | ||||
|  | ||||
|   // Implements LinuxDumper::IsPostMortem(). | ||||
|   // Always returns false to indicate this dumper performs a dump of | ||||
|   // a crashed process via ptrace. | ||||
|   virtual bool IsPostMortem() const; | ||||
|  | ||||
|   // Implements LinuxDumper::ThreadsSuspend(). | ||||
|   // Suspends all threads in the given process. Returns true on success. | ||||
|   virtual bool ThreadsSuspend(); | ||||
|  | ||||
|   // Implements LinuxDumper::ThreadsResume(). | ||||
|   // Resumes all threads in the given process. Returns true on success. | ||||
|   virtual bool ThreadsResume(); | ||||
|  | ||||
|  protected: | ||||
|   // Implements LinuxDumper::EnumerateThreads(). | ||||
|   // Enumerates all threads of the given process into |threads_|. | ||||
|   virtual bool EnumerateThreads(); | ||||
|  | ||||
|  private: | ||||
|   // Set to true if all threads of the crashed process are suspended. | ||||
|   bool threads_suspended_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_ | ||||
| @@ -0,0 +1,461 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // linux_ptrace_dumper_unittest.cc: | ||||
| // Unit tests for google_breakpad::LinuxPtraceDumper. | ||||
| // | ||||
| // This file was renamed from linux_dumper_unittest.cc and modified due | ||||
| // to LinuxDumper being splitted into two classes. | ||||
|  | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <limits.h> | ||||
| #include <unistd.h> | ||||
| #include <signal.h> | ||||
| #include <stdint.h> | ||||
| #include <string.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/prctl.h> | ||||
| #include <sys/poll.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "client/linux/minidump_writer/linux_ptrace_dumper.h" | ||||
| #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/linux/file_id.h" | ||||
| #include "common/linux/ignore_ret.h" | ||||
| #include "common/linux/safe_readlink.h" | ||||
| #include "common/memory.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| #ifndef PR_SET_PTRACER | ||||
| #define PR_SET_PTRACER 0x59616d61 | ||||
| #endif | ||||
|  | ||||
| using namespace google_breakpad; | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| typedef testing::Test LinuxPtraceDumperTest; | ||||
|  | ||||
| /* Fixture for running tests in a child process. */ | ||||
| class LinuxPtraceDumperChildTest : public testing::Test { | ||||
|  protected: | ||||
|   virtual void SetUp() { | ||||
|     child_pid_ = fork(); | ||||
| #ifndef __ANDROID__ | ||||
|     prctl(PR_SET_PTRACER, child_pid_); | ||||
| #endif | ||||
|   } | ||||
|  | ||||
|   /* Gtest is calling TestBody from this class, which sets up a child | ||||
|    * process in which the RealTestBody virtual member is called. | ||||
|    * As such, TestBody is not supposed to be overridden in derived classes. | ||||
|    */ | ||||
|   virtual void TestBody() /* final */ { | ||||
|     if (child_pid_ == 0) { | ||||
|       // child process | ||||
|       RealTestBody(); | ||||
|       exit(HasFatalFailure() ? kFatalFailure : | ||||
|            (HasNonfatalFailure() ? kNonFatalFailure : 0)); | ||||
|     } | ||||
|  | ||||
|     ASSERT_TRUE(child_pid_ > 0); | ||||
|     int status; | ||||
|     waitpid(child_pid_, &status, 0); | ||||
|     if (WEXITSTATUS(status) == kFatalFailure) { | ||||
|       GTEST_FATAL_FAILURE_("Test failed in child process"); | ||||
|     } else if (WEXITSTATUS(status) == kNonFatalFailure) { | ||||
|       GTEST_NONFATAL_FAILURE_("Test failed in child process"); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* Gtest defines TestBody functions through its macros, but classes | ||||
|    * derived from this one need to define RealTestBody instead. | ||||
|    * This is achieved by defining a TestBody macro further below. | ||||
|    */ | ||||
|   virtual void RealTestBody() = 0; | ||||
|  private: | ||||
|   static const int kFatalFailure = 1; | ||||
|   static const int kNonFatalFailure = 2; | ||||
|  | ||||
|   pid_t child_pid_; | ||||
| }; | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| /* Replace TestBody declarations within TEST*() with RealTestBody | ||||
|  * declarations */ | ||||
| #define TestBody RealTestBody | ||||
|  | ||||
| TEST_F(LinuxPtraceDumperChildTest, Setup) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
| } | ||||
|  | ||||
| TEST_F(LinuxPtraceDumperChildTest, FindMappings) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|  | ||||
|   ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(getpid))); | ||||
|   ASSERT_TRUE(dumper.FindMapping(reinterpret_cast<void*>(printf))); | ||||
|   ASSERT_FALSE(dumper.FindMapping(NULL)); | ||||
| } | ||||
|  | ||||
| TEST_F(LinuxPtraceDumperChildTest, ThreadList) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|  | ||||
|   ASSERT_GE(dumper.threads().size(), (size_t)1); | ||||
|   bool found = false; | ||||
|   for (size_t i = 0; i < dumper.threads().size(); ++i) { | ||||
|     if (dumper.threads()[i] == getppid()) { | ||||
|       ASSERT_FALSE(found); | ||||
|       found = true; | ||||
|     } | ||||
|   } | ||||
|   ASSERT_TRUE(found); | ||||
| } | ||||
|  | ||||
| // Helper stack class to close a file descriptor and unmap | ||||
| // a mmap'ed mapping. | ||||
| class StackHelper { | ||||
|  public: | ||||
|   StackHelper() | ||||
|     : fd_(-1), mapping_(NULL), size_(0) {} | ||||
|   ~StackHelper() { | ||||
|     if (size_) | ||||
|       munmap(mapping_, size_); | ||||
|     if (fd_ >= 0) | ||||
|       close(fd_); | ||||
|   } | ||||
|   void Init(int fd, char* mapping, size_t size) { | ||||
|     fd_ = fd; | ||||
|     mapping_ = mapping; | ||||
|     size_ = size; | ||||
|   } | ||||
|  | ||||
|   char* mapping() const { return mapping_; } | ||||
|   size_t size() const { return size_; } | ||||
|  | ||||
|  private: | ||||
|   int fd_; | ||||
|   char* mapping_; | ||||
|   size_t size_; | ||||
| }; | ||||
|  | ||||
| class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest { | ||||
|  protected: | ||||
|   virtual void SetUp(); | ||||
|  | ||||
|   string helper_path_; | ||||
|   size_t page_size_; | ||||
|   StackHelper helper_; | ||||
| }; | ||||
|  | ||||
| void LinuxPtraceDumperMappingsTest::SetUp() { | ||||
|   helper_path_ = GetHelperBinary(); | ||||
|   if (helper_path_.empty()) { | ||||
|     FAIL() << "Couldn't find helper binary"; | ||||
|     exit(1); | ||||
|   } | ||||
|  | ||||
|   // mmap two segments out of the helper binary, one | ||||
|   // enclosed in the other, but with different protections. | ||||
|   page_size_ = sysconf(_SC_PAGESIZE); | ||||
|   const size_t kMappingSize = 3 * page_size_; | ||||
|   int fd = open(helper_path_.c_str(), O_RDONLY); | ||||
|   ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_ | ||||
|                     << ", Error: " << strerror(errno); | ||||
|   char* mapping = | ||||
|     reinterpret_cast<char*>(mmap(NULL, | ||||
|                                  kMappingSize, | ||||
|                                  PROT_READ, | ||||
|                                  MAP_SHARED, | ||||
|                                  fd, | ||||
|                                  0)); | ||||
|   ASSERT_TRUE(mapping); | ||||
|  | ||||
|   // Ensure that things get cleaned up. | ||||
|   helper_.Init(fd, mapping, kMappingSize); | ||||
|  | ||||
|   // Carve a page out of the first mapping with different permissions. | ||||
|   char* inside_mapping =  reinterpret_cast<char*>( | ||||
|       mmap(mapping + 2 * page_size_, | ||||
|            page_size_, | ||||
|            PROT_NONE, | ||||
|            MAP_SHARED | MAP_FIXED, | ||||
|            fd, | ||||
|            // Map a different offset just to | ||||
|            // better test real-world conditions. | ||||
|            page_size_)); | ||||
|   ASSERT_TRUE(inside_mapping); | ||||
|  | ||||
|   LinuxPtraceDumperChildTest::SetUp(); | ||||
| } | ||||
|  | ||||
| TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) { | ||||
|   // Now check that LinuxPtraceDumper interpreted the mappings properly. | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|   int mapping_count = 0; | ||||
|   for (unsigned i = 0; i < dumper.mappings().size(); ++i) { | ||||
|     const MappingInfo& mapping = *dumper.mappings()[i]; | ||||
|     if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) { | ||||
|       // This mapping should encompass the entire original mapped | ||||
|       // range. | ||||
|       EXPECT_EQ(reinterpret_cast<uintptr_t>(this->helper_.mapping()), | ||||
|                 mapping.start_addr); | ||||
|       EXPECT_EQ(this->helper_.size(), mapping.size); | ||||
|       EXPECT_EQ(0U, mapping.offset); | ||||
|       mapping_count++; | ||||
|     } | ||||
|   } | ||||
|   EXPECT_EQ(1, mapping_count); | ||||
| } | ||||
|  | ||||
| TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) { | ||||
|   const pid_t pid = getppid(); | ||||
|   LinuxPtraceDumper dumper(pid); | ||||
|  | ||||
|   char maps_path[NAME_MAX] = ""; | ||||
|   char maps_path_expected[NAME_MAX]; | ||||
|   snprintf(maps_path_expected, sizeof(maps_path_expected), | ||||
|            "/proc/%d/maps", pid); | ||||
|   EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); | ||||
|   EXPECT_STREQ(maps_path_expected, maps_path); | ||||
|  | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); | ||||
|  | ||||
|   char long_node[NAME_MAX]; | ||||
|   size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1; | ||||
|   memset(long_node, 'a', long_node_len); | ||||
|   long_node[long_node_len] = '\0'; | ||||
|   EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); | ||||
| } | ||||
|  | ||||
| #if !defined(__ARM_EABI__) && !defined(__mips__) | ||||
| // Ensure that the linux-gate VDSO is included in the mapping list. | ||||
| TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|  | ||||
|   void* linux_gate_loc = | ||||
|     reinterpret_cast<void *>(dumper.auxv()[AT_SYSINFO_EHDR]); | ||||
|   ASSERT_TRUE(linux_gate_loc); | ||||
|   bool found_linux_gate = false; | ||||
|  | ||||
|   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||
|   const MappingInfo* mapping; | ||||
|   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||
|     mapping = mappings[i]; | ||||
|     if (!strcmp(mapping->name, kLinuxGateLibraryName)) { | ||||
|       found_linux_gate = true; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   EXPECT_TRUE(found_linux_gate); | ||||
|   EXPECT_EQ(linux_gate_loc, reinterpret_cast<void*>(mapping->start_addr)); | ||||
|   EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); | ||||
| } | ||||
|  | ||||
| // Ensure that the linux-gate VDSO can generate a non-zeroed File ID. | ||||
| TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) { | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|  | ||||
|   bool found_linux_gate = false; | ||||
|   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||
|   unsigned index = 0; | ||||
|   for (unsigned i = 0; i < mappings.size(); ++i) { | ||||
|     if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { | ||||
|       found_linux_gate = true; | ||||
|       index = i; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   ASSERT_TRUE(found_linux_gate); | ||||
|  | ||||
|   // Need to suspend the child so ptrace actually works. | ||||
|   ASSERT_TRUE(dumper.ThreadsSuspend()); | ||||
|   uint8_t identifier[sizeof(MDGUID)]; | ||||
|   ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], | ||||
|                                                  true, | ||||
|                                                  index, | ||||
|                                                  identifier)); | ||||
|   uint8_t empty_identifier[sizeof(MDGUID)]; | ||||
|   memset(empty_identifier, 0, sizeof(empty_identifier)); | ||||
|   EXPECT_NE(0, memcmp(empty_identifier, identifier, sizeof(identifier))); | ||||
|   EXPECT_TRUE(dumper.ThreadsResume()); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) { | ||||
|   // Calculate the File ID of our binary using both | ||||
|   // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping | ||||
|   // and ensure that we get the same result from both. | ||||
|   char exe_name[PATH_MAX]; | ||||
|   ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); | ||||
|  | ||||
|   LinuxPtraceDumper dumper(getppid()); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|   const wasteful_vector<MappingInfo*> mappings = dumper.mappings(); | ||||
|   bool found_exe = false; | ||||
|   unsigned i; | ||||
|   for (i = 0; i < mappings.size(); ++i) { | ||||
|     const MappingInfo* mapping = mappings[i]; | ||||
|     if (!strcmp(mapping->name, exe_name)) { | ||||
|       found_exe = true; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   ASSERT_TRUE(found_exe); | ||||
|  | ||||
|   uint8_t identifier1[sizeof(MDGUID)]; | ||||
|   uint8_t identifier2[sizeof(MDGUID)]; | ||||
|   EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i, | ||||
|                                                  identifier1)); | ||||
|   FileID fileid(exe_name); | ||||
|   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); | ||||
|   char identifier_string1[37]; | ||||
|   char identifier_string2[37]; | ||||
|   FileID::ConvertIdentifierToString(identifier1, identifier_string1, | ||||
|                                     37); | ||||
|   FileID::ConvertIdentifierToString(identifier2, identifier_string2, | ||||
|                                     37); | ||||
|   EXPECT_STREQ(identifier_string1, identifier_string2); | ||||
| } | ||||
|  | ||||
| /* Get back to normal behavior of TEST*() macros wrt TestBody. */ | ||||
| #undef TestBody | ||||
|  | ||||
| TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { | ||||
|   static const int kNumberOfThreadsInHelperProgram = 5; | ||||
|   char kNumberOfThreadsArgument[2]; | ||||
|   sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); | ||||
|  | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   pid_t child_pid = fork(); | ||||
|   if (child_pid == 0) { | ||||
|     // In child process. | ||||
|     close(fds[0]); | ||||
|  | ||||
|     string helper_path(GetHelperBinary()); | ||||
|     if (helper_path.empty()) { | ||||
|       FAIL() << "Couldn't find helper binary"; | ||||
|       exit(1); | ||||
|     } | ||||
|  | ||||
|     // Pass the pipe fd and the number of threads as arguments. | ||||
|     char pipe_fd_string[8]; | ||||
|     sprintf(pipe_fd_string, "%d", fds[1]); | ||||
|     execl(helper_path.c_str(), | ||||
|           "linux_dumper_unittest_helper", | ||||
|           pipe_fd_string, | ||||
|           kNumberOfThreadsArgument, | ||||
|           NULL); | ||||
|     // Kill if we get here. | ||||
|     printf("Errno from exec: %d", errno); | ||||
|     FAIL() << "Exec of " << helper_path << " failed: " << strerror(errno); | ||||
|     exit(0); | ||||
|   } | ||||
|   close(fds[1]); | ||||
|  | ||||
|   // Wait for all child threads to indicate that they have started | ||||
|   for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { | ||||
|     struct pollfd pfd; | ||||
|     memset(&pfd, 0, sizeof(pfd)); | ||||
|     pfd.fd = fds[0]; | ||||
|     pfd.events = POLLIN | POLLERR; | ||||
|  | ||||
|     const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); | ||||
|     ASSERT_EQ(1, r); | ||||
|     ASSERT_TRUE(pfd.revents & POLLIN); | ||||
|     uint8_t junk; | ||||
|     ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), | ||||
|               static_cast<ssize_t>(sizeof(junk))); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   // There is a race here because we may stop a child thread before | ||||
|   // it is actually running the busy loop. Empirically this sleep | ||||
|   // is sufficient to avoid the race. | ||||
|   usleep(100000); | ||||
|  | ||||
|   // Children are ready now. | ||||
|   LinuxPtraceDumper dumper(child_pid); | ||||
|   ASSERT_TRUE(dumper.Init()); | ||||
|   EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size()); | ||||
|   EXPECT_TRUE(dumper.ThreadsSuspend()); | ||||
|  | ||||
|   ThreadInfo one_thread; | ||||
|   for (size_t i = 0; i < dumper.threads().size(); ++i) { | ||||
|     EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread)); | ||||
|     const void* stack; | ||||
|     size_t stack_len; | ||||
|     EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, | ||||
|         one_thread.stack_pointer)); | ||||
|     // In the helper program, we stored a pointer to the thread id in a | ||||
|     // specific register. Check that we can recover its value. | ||||
| #if defined(__ARM_EABI__) | ||||
|     pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]); | ||||
| #elif defined(__i386) | ||||
|     pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx); | ||||
| #elif defined(__x86_64) | ||||
|     pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx); | ||||
| #elif defined(__mips__) | ||||
|     pid_t* process_tid_location = | ||||
|         reinterpret_cast<pid_t*>(one_thread.regs.regs[1]); | ||||
| #else | ||||
| #error This test has not been ported to this platform. | ||||
| #endif | ||||
|     pid_t one_thread_id; | ||||
|     dumper.CopyFromProcess(&one_thread_id, | ||||
|                            dumper.threads()[i], | ||||
|                            process_tid_location, | ||||
|                            4); | ||||
|     EXPECT_EQ(dumper.threads()[i], one_thread_id); | ||||
|   } | ||||
|   EXPECT_TRUE(dumper.ThreadsResume()); | ||||
|   kill(child_pid, SIGKILL); | ||||
|  | ||||
|   // Reap child | ||||
|   int status; | ||||
|   ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0))); | ||||
|   ASSERT_TRUE(WIFSIGNALED(status)); | ||||
|   ASSERT_EQ(SIGKILL, WTERMSIG(status)); | ||||
| } | ||||
							
								
								
									
										1852
									
								
								sdk/breakpad/client/linux/minidump_writer/minidump_writer.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1852
									
								
								sdk/breakpad/client/linux/minidump_writer/minidump_writer.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										125
									
								
								sdk/breakpad/client/linux/minidump_writer/minidump_writer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								sdk/breakpad/client/linux/minidump_writer/minidump_writer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -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. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <sys/types.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <list> | ||||
| #include <utility> | ||||
|  | ||||
| #include "client/linux/minidump_writer/linux_dumper.h" | ||||
| #include "google_breakpad/common/minidump_format.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class ExceptionHandler; | ||||
|  | ||||
| struct MappingEntry { | ||||
|   MappingInfo first; | ||||
|   uint8_t second[sizeof(MDGUID)]; | ||||
| }; | ||||
|  | ||||
| // A list of <MappingInfo, GUID> | ||||
| typedef std::list<MappingEntry> MappingList; | ||||
|  | ||||
| // These entries store a list of memory regions that the client wants included | ||||
| // in the minidump. | ||||
| struct AppMemory { | ||||
|   void* ptr; | ||||
|   size_t length; | ||||
|  | ||||
|   bool operator==(const struct AppMemory& other) const { | ||||
|     return ptr == other.ptr; | ||||
|   } | ||||
|  | ||||
|   bool operator==(const void* other) const { | ||||
|     return ptr == other; | ||||
|   } | ||||
| }; | ||||
| typedef std::list<AppMemory> AppMemoryList; | ||||
|  | ||||
| // Writes a minidump to the filesystem. These functions do not malloc nor use | ||||
| // libc functions which may. Thus, it can be used in contexts where the state | ||||
| // of the heap may be corrupt. | ||||
| //   minidump_path: the path to the file to write to. This is opened O_EXCL and | ||||
| //     fails open fails. | ||||
| //   crashing_process: the pid of the crashing process. This must be trusted. | ||||
| //   blob: a blob of data from the crashing process. See exception_handler.h | ||||
| //   blob_size: the length of |blob|, in bytes | ||||
| // | ||||
| // Returns true iff successful. | ||||
| bool WriteMinidump(const char* minidump_path, pid_t crashing_process, | ||||
|                    const void* blob, size_t blob_size); | ||||
| // Same as above but takes an open file descriptor instead of a path. | ||||
| bool WriteMinidump(int minidump_fd, pid_t crashing_process, | ||||
|                    const void* blob, size_t blob_size); | ||||
|  | ||||
| // Alternate form of WriteMinidump() that works with processes that | ||||
| // are not expected to have crashed.  If |process_blamed_thread| is | ||||
| // meaningful, it will be the one from which a crash signature is | ||||
| // extracted.  It is not expected that this function will be called | ||||
| // from a compromised context, but it is safe to do so. | ||||
| bool WriteMinidump(const char* minidump_path, pid_t process, | ||||
|                    pid_t process_blamed_thread); | ||||
|  | ||||
| // These overloads also allow passing a list of known mappings and | ||||
| // a list of additional memory regions to be included in the minidump. | ||||
| bool WriteMinidump(const char* minidump_path, pid_t crashing_process, | ||||
|                    const void* blob, size_t blob_size, | ||||
|                    const MappingList& mappings, | ||||
|                    const AppMemoryList& appdata); | ||||
| bool WriteMinidump(int minidump_fd, pid_t crashing_process, | ||||
|                    const void* blob, size_t blob_size, | ||||
|                    const MappingList& mappings, | ||||
|                    const AppMemoryList& appdata); | ||||
|  | ||||
| // These overloads also allow passing a file size limit for the minidump. | ||||
| bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit, | ||||
|                    pid_t crashing_process, | ||||
|                    const void* blob, size_t blob_size, | ||||
|                    const MappingList& mappings, | ||||
|                    const AppMemoryList& appdata); | ||||
| bool WriteMinidump(int minidump_fd, off_t minidump_size_limit, | ||||
|                    pid_t crashing_process, | ||||
|                    const void* blob, size_t blob_size, | ||||
|                    const MappingList& mappings, | ||||
|                    const AppMemoryList& appdata); | ||||
|  | ||||
| bool WriteMinidump(const char* filename, | ||||
|                    const MappingList& mappings, | ||||
|                    const AppMemoryList& appdata, | ||||
|                    LinuxDumper* dumper); | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ | ||||
| @@ -0,0 +1,763 @@ | ||||
| // Copyright (c) 2011 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <fcntl.h> | ||||
| #include <sys/poll.h> | ||||
| #include <sys/stat.h> | ||||
| #include <sys/syscall.h> | ||||
| #include <sys/types.h> | ||||
| #include <ucontext.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "client/linux/handler/exception_handler.h" | ||||
| #include "client/linux/minidump_writer/linux_dumper.h" | ||||
| #include "client/linux/minidump_writer/minidump_writer.h" | ||||
| #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" | ||||
| #include "common/linux/eintr_wrapper.h" | ||||
| #include "common/linux/file_id.h" | ||||
| #include "common/linux/ignore_ret.h" | ||||
| #include "common/linux/safe_readlink.h" | ||||
| #include "common/scoped_ptr.h" | ||||
| #include "common/tests/auto_tempdir.h" | ||||
| #include "common/tests/file_utils.h" | ||||
| #include "common/using_std_string.h" | ||||
| #include "google_breakpad/processor/minidump.h" | ||||
|  | ||||
| using namespace google_breakpad; | ||||
|  | ||||
| // Length of a formatted GUID string = | ||||
| // sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator) | ||||
| const int kGUIDStringSize = 37; | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| typedef testing::Test MinidumpWriterTest; | ||||
|  | ||||
| const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest"; | ||||
|  | ||||
| TEST(MinidumpWriterTest, SetupWithPath) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     char b; | ||||
|     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   ExceptionHandler::CrashContext context; | ||||
|   memset(&context, 0, sizeof(context)); | ||||
|  | ||||
|   AutoTempDir temp_dir; | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   // Set a non-zero tid to avoid tripping asserts. | ||||
|   context.tid = child; | ||||
|   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); | ||||
|   struct stat st; | ||||
|   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||
|   ASSERT_GT(st.st_size, 0); | ||||
|  | ||||
|   close(fds[1]); | ||||
| } | ||||
|  | ||||
| TEST(MinidumpWriterTest, SetupWithFD) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     char b; | ||||
|     HANDLE_EINTR(read(fds[0], &b, sizeof(b))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   ExceptionHandler::CrashContext context; | ||||
|   memset(&context, 0, sizeof(context)); | ||||
|  | ||||
|   AutoTempDir temp_dir; | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU); | ||||
|   // Set a non-zero tid to avoid tripping asserts. | ||||
|   context.tid = child; | ||||
|   ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context))); | ||||
|   struct stat st; | ||||
|   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||
|   ASSERT_GT(st.st_size, 0); | ||||
|  | ||||
|   close(fds[1]); | ||||
| } | ||||
|  | ||||
| // Test that mapping info can be specified when writing a minidump, | ||||
| // and that it ends up in the module list of the minidump. | ||||
| TEST(MinidumpWriterTest, MappingInfo) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   // These are defined here so the parent can use them to check the | ||||
|   // data from the minidump afterwards. | ||||
|   const uint32_t memory_size = sysconf(_SC_PAGESIZE); | ||||
|   const char* kMemoryName = "a fake module"; | ||||
|   const uint8_t kModuleGUID[sizeof(MDGUID)] = { | ||||
|     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, | ||||
|     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF | ||||
|   }; | ||||
|   char module_identifier_buffer[kGUIDStringSize]; | ||||
|   FileID::ConvertIdentifierToString(kModuleGUID, | ||||
|                                     module_identifier_buffer, | ||||
|                                     sizeof(module_identifier_buffer)); | ||||
|   string module_identifier(module_identifier_buffer); | ||||
|   // Strip out dashes | ||||
|   size_t pos; | ||||
|   while ((pos = module_identifier.find('-')) != string::npos) { | ||||
|     module_identifier.erase(pos, 1); | ||||
|   } | ||||
|   // And append a zero, because module IDs include an "age" field | ||||
|   // which is always zero on Linux. | ||||
|   module_identifier += "0"; | ||||
|  | ||||
|   // Get some memory. | ||||
|   char* memory = | ||||
|     reinterpret_cast<char*>(mmap(NULL, | ||||
|                                  memory_size, | ||||
|                                  PROT_READ | PROT_WRITE, | ||||
|                                  MAP_PRIVATE | MAP_ANON, | ||||
|                                  -1, | ||||
|                                  0)); | ||||
|   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); | ||||
|   ASSERT_TRUE(memory); | ||||
|  | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     char b; | ||||
|     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   ExceptionHandler::CrashContext context; | ||||
|   memset(&context, 0, sizeof(context)); | ||||
|   ASSERT_EQ(0, getcontext(&context.context)); | ||||
|   context.tid = child; | ||||
|  | ||||
|   AutoTempDir temp_dir; | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|  | ||||
|   // Add information about the mapped memory. | ||||
|   MappingInfo info; | ||||
|   info.start_addr = kMemoryAddress; | ||||
|   info.size = memory_size; | ||||
|   info.offset = 0; | ||||
|   strcpy(info.name, kMemoryName); | ||||
|  | ||||
|   MappingList mappings; | ||||
|   AppMemoryList memory_list; | ||||
|   MappingEntry mapping; | ||||
|   mapping.first = info; | ||||
|   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); | ||||
|   mappings.push_back(mapping); | ||||
|   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), | ||||
|                             mappings, memory_list)); | ||||
|  | ||||
|   // Read the minidump. Load the module list, and ensure that | ||||
|   // the mmap'ed |memory| is listed with the given module name | ||||
|   // and debug ID. | ||||
|   Minidump minidump(templ); | ||||
|   ASSERT_TRUE(minidump.Read()); | ||||
|  | ||||
|   MinidumpModuleList* module_list = minidump.GetModuleList(); | ||||
|   ASSERT_TRUE(module_list); | ||||
|   const MinidumpModule* module = | ||||
|     module_list->GetModuleForAddress(kMemoryAddress); | ||||
|   ASSERT_TRUE(module); | ||||
|  | ||||
|   EXPECT_EQ(kMemoryAddress, module->base_address()); | ||||
|   EXPECT_EQ(memory_size, module->size()); | ||||
|   EXPECT_EQ(kMemoryName, module->code_file()); | ||||
|   EXPECT_EQ(module_identifier, module->debug_identifier()); | ||||
|  | ||||
|   uint32_t len; | ||||
|   // These streams are expected to be there | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len)); | ||||
|   EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len)); | ||||
|  | ||||
|   close(fds[1]); | ||||
| } | ||||
|  | ||||
| // Test that mapping info can be specified, and that it overrides | ||||
| // existing mappings that are wholly contained within the specified | ||||
| // range. | ||||
| TEST(MinidumpWriterTest, MappingInfoContained) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   // These are defined here so the parent can use them to check the | ||||
|   // data from the minidump afterwards. | ||||
|   const int32_t memory_size = sysconf(_SC_PAGESIZE); | ||||
|   const char* kMemoryName = "a fake module"; | ||||
|   const uint8_t kModuleGUID[sizeof(MDGUID)] = { | ||||
|     0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, | ||||
|     0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF | ||||
|   }; | ||||
|   char module_identifier_buffer[kGUIDStringSize]; | ||||
|   FileID::ConvertIdentifierToString(kModuleGUID, | ||||
|                                     module_identifier_buffer, | ||||
|                                     sizeof(module_identifier_buffer)); | ||||
|   string module_identifier(module_identifier_buffer); | ||||
|   // Strip out dashes | ||||
|   size_t pos; | ||||
|   while ((pos = module_identifier.find('-')) != string::npos) { | ||||
|     module_identifier.erase(pos, 1); | ||||
|   } | ||||
|   // And append a zero, because module IDs include an "age" field | ||||
|   // which is always zero on Linux. | ||||
|   module_identifier += "0"; | ||||
|  | ||||
|   // mmap a file | ||||
|   AutoTempDir temp_dir; | ||||
|   string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp"; | ||||
|   int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0); | ||||
|   ASSERT_NE(-1, fd); | ||||
|   unlink(tempfile.c_str()); | ||||
|   // fill with zeros | ||||
|   google_breakpad::scoped_array<char> buffer(new char[memory_size]); | ||||
|   memset(buffer.get(), 0, memory_size); | ||||
|   ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size)); | ||||
|   lseek(fd, 0, SEEK_SET); | ||||
|  | ||||
|   char* memory = | ||||
|     reinterpret_cast<char*>(mmap(NULL, | ||||
|                                  memory_size, | ||||
|                                  PROT_READ | PROT_WRITE, | ||||
|                                  MAP_PRIVATE, | ||||
|                                  fd, | ||||
|                                  0)); | ||||
|   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); | ||||
|   ASSERT_TRUE(memory); | ||||
|   close(fd); | ||||
|  | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     char b; | ||||
|     IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   ExceptionHandler::CrashContext context; | ||||
|   memset(&context, 0, sizeof(context)); | ||||
|   context.tid = 1; | ||||
|  | ||||
|   string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|  | ||||
|   // Add information about the mapped memory. Report it as being larger than | ||||
|   // it actually is. | ||||
|   MappingInfo info; | ||||
|   info.start_addr = kMemoryAddress - memory_size; | ||||
|   info.size = memory_size * 3; | ||||
|   info.offset = 0; | ||||
|   strcpy(info.name, kMemoryName); | ||||
|  | ||||
|   MappingList mappings; | ||||
|   AppMemoryList memory_list; | ||||
|   MappingEntry mapping; | ||||
|   mapping.first = info; | ||||
|   memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); | ||||
|   mappings.push_back(mapping); | ||||
|   ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context), | ||||
|                             mappings, memory_list)); | ||||
|  | ||||
|   // Read the minidump. Load the module list, and ensure that | ||||
|   // the mmap'ed |memory| is listed with the given module name | ||||
|   // and debug ID. | ||||
|   Minidump minidump(dumpfile); | ||||
|   ASSERT_TRUE(minidump.Read()); | ||||
|  | ||||
|   MinidumpModuleList* module_list = minidump.GetModuleList(); | ||||
|   ASSERT_TRUE(module_list); | ||||
|   const MinidumpModule* module = | ||||
|     module_list->GetModuleForAddress(kMemoryAddress); | ||||
|   ASSERT_TRUE(module); | ||||
|  | ||||
|   EXPECT_EQ(info.start_addr, module->base_address()); | ||||
|   EXPECT_EQ(info.size, module->size()); | ||||
|   EXPECT_EQ(kMemoryName, module->code_file()); | ||||
|   EXPECT_EQ(module_identifier, module->debug_identifier()); | ||||
|  | ||||
|   close(fds[1]); | ||||
| } | ||||
|  | ||||
| TEST(MinidumpWriterTest, DeletedBinary) { | ||||
|   const string kNumberOfThreadsArgument = "1"; | ||||
|   const string helper_path(GetHelperBinary()); | ||||
|   if (helper_path.empty()) { | ||||
|     FAIL() << "Couldn't find helper binary"; | ||||
|     exit(1); | ||||
|   } | ||||
|  | ||||
|   // Copy binary to a temp file. | ||||
|   AutoTempDir temp_dir; | ||||
|   string binpath = temp_dir.path() + "/linux-dumper-unittest-helper"; | ||||
|   ASSERT_TRUE(CopyFile(helper_path.c_str(), binpath.c_str())) | ||||
|       << "Failed to copy " << helper_path << " to " << binpath; | ||||
|   ASSERT_EQ(0, chmod(binpath.c_str(), 0755)); | ||||
|  | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   pid_t child_pid = fork(); | ||||
|   if (child_pid == 0) { | ||||
|     // In child process. | ||||
|     close(fds[0]); | ||||
|  | ||||
|     // Pass the pipe fd and the number of threads as arguments. | ||||
|     char pipe_fd_string[8]; | ||||
|     sprintf(pipe_fd_string, "%d", fds[1]); | ||||
|     execl(binpath.c_str(), | ||||
|           binpath.c_str(), | ||||
|           pipe_fd_string, | ||||
|           kNumberOfThreadsArgument.c_str(), | ||||
|           NULL); | ||||
|   } | ||||
|   close(fds[1]); | ||||
|   // Wait for the child process to signal that it's ready. | ||||
|   struct pollfd pfd; | ||||
|   memset(&pfd, 0, sizeof(pfd)); | ||||
|   pfd.fd = fds[0]; | ||||
|   pfd.events = POLLIN | POLLERR; | ||||
|  | ||||
|   const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); | ||||
|   ASSERT_EQ(1, r); | ||||
|   ASSERT_TRUE(pfd.revents & POLLIN); | ||||
|   uint8_t junk; | ||||
|   const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk))); | ||||
|   ASSERT_EQ(static_cast<ssize_t>(sizeof(junk)), nr); | ||||
|   close(fds[0]); | ||||
|  | ||||
|   // Child is ready now. | ||||
|   // Unlink the test binary. | ||||
|   unlink(binpath.c_str()); | ||||
|  | ||||
|   ExceptionHandler::CrashContext context; | ||||
|   memset(&context, 0, sizeof(context)); | ||||
|  | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   // Set a non-zero tid to avoid tripping asserts. | ||||
|   context.tid = child_pid; | ||||
|   ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context, | ||||
|                             sizeof(context))); | ||||
|   kill(child_pid, SIGKILL); | ||||
|  | ||||
|   struct stat st; | ||||
|   ASSERT_EQ(0, stat(templ.c_str(), &st)); | ||||
|   ASSERT_GT(st.st_size, 0); | ||||
|  | ||||
|   Minidump minidump(templ); | ||||
|   ASSERT_TRUE(minidump.Read()); | ||||
|  | ||||
|   // Check that the main module filename is correct. | ||||
|   MinidumpModuleList* module_list = minidump.GetModuleList(); | ||||
|   ASSERT_TRUE(module_list); | ||||
|   const MinidumpModule* module = module_list->GetMainModule(); | ||||
|   EXPECT_STREQ(binpath.c_str(), module->code_file().c_str()); | ||||
|   // Check that the file ID is correct. | ||||
|   FileID fileid(helper_path.c_str()); | ||||
|   uint8_t identifier[sizeof(MDGUID)]; | ||||
|   EXPECT_TRUE(fileid.ElfFileIdentifier(identifier)); | ||||
|   char identifier_string[kGUIDStringSize]; | ||||
|   FileID::ConvertIdentifierToString(identifier, | ||||
|                                     identifier_string, | ||||
|                                     kGUIDStringSize); | ||||
|   string module_identifier(identifier_string); | ||||
|   // Strip out dashes | ||||
|   size_t pos; | ||||
|   while ((pos = module_identifier.find('-')) != string::npos) { | ||||
|     module_identifier.erase(pos, 1); | ||||
|   } | ||||
|   // And append a zero, because module IDs include an "age" field | ||||
|   // which is always zero on Linux. | ||||
|   module_identifier += "0"; | ||||
|   EXPECT_EQ(module_identifier, module->debug_identifier()); | ||||
| } | ||||
|  | ||||
| // Test that an additional memory region can be added to the minidump. | ||||
| TEST(MinidumpWriterTest, AdditionalMemory) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   // These are defined here so the parent can use them to check the | ||||
|   // data from the minidump afterwards. | ||||
|   const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); | ||||
|  | ||||
|   // Get some heap memory. | ||||
|   uint8_t* memory = new uint8_t[kMemorySize]; | ||||
|   const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory); | ||||
|   ASSERT_TRUE(memory); | ||||
|  | ||||
|   // Stick some data into the memory so the contents can be verified. | ||||
|   for (uint32_t i = 0; i < kMemorySize; ++i) { | ||||
|     memory[i] = i % 255; | ||||
|   } | ||||
|  | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     char b; | ||||
|     HANDLE_EINTR(read(fds[0], &b, sizeof(b))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   ExceptionHandler::CrashContext context; | ||||
|  | ||||
|   // This needs a valid context for minidump writing to work, but getting | ||||
|   // a useful one from the child is too much work, so just use one from | ||||
|   // the parent since the child is just a forked copy anyway. | ||||
|   ASSERT_EQ(0, getcontext(&context.context)); | ||||
|   context.tid = child; | ||||
|  | ||||
|   AutoTempDir temp_dir; | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   unlink(templ.c_str()); | ||||
|  | ||||
|   MappingList mappings; | ||||
|   AppMemoryList memory_list; | ||||
|  | ||||
|   // Add the memory region to the list of memory to be included. | ||||
|   AppMemory app_memory; | ||||
|   app_memory.ptr = memory; | ||||
|   app_memory.length = kMemorySize; | ||||
|   memory_list.push_back(app_memory); | ||||
|   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), | ||||
|                             mappings, memory_list)); | ||||
|  | ||||
|   // Read the minidump. Ensure that the memory region is present | ||||
|   Minidump minidump(templ); | ||||
|   ASSERT_TRUE(minidump.Read()); | ||||
|  | ||||
|   MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); | ||||
|   ASSERT_TRUE(dump_memory_list); | ||||
|   const MinidumpMemoryRegion* region = | ||||
|     dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); | ||||
|   ASSERT_TRUE(region); | ||||
|  | ||||
|   EXPECT_EQ(kMemoryAddress, region->GetBase()); | ||||
|   EXPECT_EQ(kMemorySize, region->GetSize()); | ||||
|  | ||||
|   // Verify memory contents. | ||||
|   EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); | ||||
|  | ||||
|   delete[] memory; | ||||
|   close(fds[1]); | ||||
| } | ||||
|  | ||||
| // Test that an invalid thread stack pointer still results in a minidump. | ||||
| TEST(MinidumpWriterTest, InvalidStackPointer) { | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   const pid_t child = fork(); | ||||
|   if (child == 0) { | ||||
|     close(fds[1]); | ||||
|     char b; | ||||
|     HANDLE_EINTR(read(fds[0], &b, sizeof(b))); | ||||
|     close(fds[0]); | ||||
|     syscall(__NR_exit); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   ExceptionHandler::CrashContext context; | ||||
|  | ||||
|   // This needs a valid context for minidump writing to work, but getting | ||||
|   // a useful one from the child is too much work, so just use one from | ||||
|   // the parent since the child is just a forked copy anyway. | ||||
|   ASSERT_EQ(0, getcontext(&context.context)); | ||||
|   context.tid = child; | ||||
|  | ||||
|   // Fake the child's stack pointer for its crashing thread.  NOTE: This must | ||||
|   // be an invalid memory address for the child process (stack or otherwise). | ||||
| #if defined(__i386) | ||||
|   // Try 1MB below the current stack. | ||||
|   uintptr_t invalid_stack_pointer = | ||||
|       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||
|   context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer; | ||||
| #elif defined(__x86_64) | ||||
|   // Try 1MB below the current stack. | ||||
|   uintptr_t invalid_stack_pointer = | ||||
|       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||
|   context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer; | ||||
| #elif defined(__ARM_EABI__) | ||||
|   // Try 1MB below the current stack. | ||||
|   uintptr_t invalid_stack_pointer = | ||||
|       reinterpret_cast<uintptr_t>(&context) - 1024*1024; | ||||
|   context.context.uc_mcontext.arm_sp = invalid_stack_pointer; | ||||
| #elif defined(__mips__) | ||||
|   // Try 1MB below the current stack. | ||||
|   uintptr_t invalid_stack_pointer = | ||||
|       reinterpret_cast<uintptr_t>(&context) - 1024 * 1024; | ||||
|   context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] =  | ||||
|       invalid_stack_pointer; | ||||
| #else | ||||
| # error "This code has not been ported to your platform yet." | ||||
| #endif | ||||
|  | ||||
|   AutoTempDir temp_dir; | ||||
|   string templ = temp_dir.path() + kMDWriterUnitTestFileName; | ||||
|   // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if | ||||
|   // presented with an invalid stack pointer. | ||||
|   ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); | ||||
|  | ||||
|   // Read the minidump. Ensure that the memory region is present | ||||
|   Minidump minidump(templ); | ||||
|   ASSERT_TRUE(minidump.Read()); | ||||
|  | ||||
|   // TODO(ted.mielczarek,mkrebs): Enable this part of the test once | ||||
|   // https://breakpad.appspot.com/413002/ is committed. | ||||
| #if 0 | ||||
|   // Make sure there's a thread without a stack.  NOTE: It's okay if | ||||
|   // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory | ||||
|   // region problem". | ||||
|   MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); | ||||
|   ASSERT_TRUE(dump_thread_list); | ||||
|   bool found_empty_stack = false; | ||||
|   for (int i = 0; i < dump_thread_list->thread_count(); i++) { | ||||
|     MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); | ||||
|     ASSERT_TRUE(thread->thread() != NULL); | ||||
|     // When the stack size is zero bytes, GetMemory() returns NULL. | ||||
|     if (thread->GetMemory() == NULL) { | ||||
|       found_empty_stack = true; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|   // NOTE: If you fail this, first make sure that "invalid_stack_pointer" | ||||
|   // above is indeed set to an invalid address. | ||||
|   ASSERT_TRUE(found_empty_stack); | ||||
| #endif | ||||
|  | ||||
|   close(fds[1]); | ||||
| } | ||||
|  | ||||
| // Test that limiting the size of the minidump works. | ||||
| TEST(MinidumpWriterTest, MinidumpSizeLimit) { | ||||
|   static const int kNumberOfThreadsInHelperProgram = 40; | ||||
|  | ||||
|   char number_of_threads_arg[3]; | ||||
|   sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram); | ||||
|  | ||||
|   string helper_path(GetHelperBinary()); | ||||
|   if (helper_path.empty()) { | ||||
|     FAIL() << "Couldn't find helper binary"; | ||||
|     exit(1); | ||||
|   } | ||||
|  | ||||
|   int fds[2]; | ||||
|   ASSERT_NE(-1, pipe(fds)); | ||||
|  | ||||
|   pid_t child_pid = fork(); | ||||
|   if (child_pid == 0) { | ||||
|     // In child process. | ||||
|     close(fds[0]); | ||||
|  | ||||
|     // Pass the pipe fd and the number of threads as arguments. | ||||
|     char pipe_fd_string[8]; | ||||
|     sprintf(pipe_fd_string, "%d", fds[1]); | ||||
|     execl(helper_path.c_str(), | ||||
|           helper_path.c_str(), | ||||
|           pipe_fd_string, | ||||
|           number_of_threads_arg, | ||||
|           NULL); | ||||
|   } | ||||
|   close(fds[1]); | ||||
|  | ||||
|   // Wait for all child threads to indicate that they have started | ||||
|   for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { | ||||
|     struct pollfd pfd; | ||||
|     memset(&pfd, 0, sizeof(pfd)); | ||||
|     pfd.fd = fds[0]; | ||||
|     pfd.events = POLLIN | POLLERR; | ||||
|  | ||||
|     const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); | ||||
|     ASSERT_EQ(1, r); | ||||
|     ASSERT_TRUE(pfd.revents & POLLIN); | ||||
|     uint8_t junk; | ||||
|     ASSERT_EQ(read(fds[0], &junk, sizeof(junk)),  | ||||
|               static_cast<ssize_t>(sizeof(junk))); | ||||
|   } | ||||
|   close(fds[0]); | ||||
|  | ||||
|   // There is a race here because we may stop a child thread before | ||||
|   // it is actually running the busy loop. Empirically this sleep | ||||
|   // is sufficient to avoid the race. | ||||
|   usleep(100000); | ||||
|  | ||||
|   // Child and its threads are ready now. | ||||
|  | ||||
|  | ||||
|   off_t normal_file_size; | ||||
|   int total_normal_stack_size = 0; | ||||
|   AutoTempDir temp_dir; | ||||
|  | ||||
|   // First, write a minidump with no size limit. | ||||
|   { | ||||
|     string normal_dump = temp_dir.path() + | ||||
|         "/minidump-writer-unittest.dmp"; | ||||
|     ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1, | ||||
|                               child_pid, NULL, 0, | ||||
|                               MappingList(), AppMemoryList())); | ||||
|     struct stat st; | ||||
|     ASSERT_EQ(0, stat(normal_dump.c_str(), &st)); | ||||
|     ASSERT_GT(st.st_size, 0); | ||||
|     normal_file_size = st.st_size; | ||||
|  | ||||
|     Minidump minidump(normal_dump); | ||||
|     ASSERT_TRUE(minidump.Read()); | ||||
|     MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); | ||||
|     ASSERT_TRUE(dump_thread_list); | ||||
|     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { | ||||
|       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); | ||||
|       ASSERT_TRUE(thread->thread() != NULL); | ||||
|       // When the stack size is zero bytes, GetMemory() returns NULL. | ||||
|       MinidumpMemoryRegion* memory = thread->GetMemory(); | ||||
|       ASSERT_TRUE(memory != NULL); | ||||
|       total_normal_stack_size += memory->GetSize(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Second, write a minidump with a size limit big enough to not trigger | ||||
|   // anything. | ||||
|   { | ||||
|     // Set size limit arbitrarily 1MB larger than the normal file size -- such | ||||
|     // that the limiting code will not kick in. | ||||
|     const off_t minidump_size_limit = normal_file_size + 1024*1024; | ||||
|  | ||||
|     string same_dump = temp_dir.path() + | ||||
|         "/minidump-writer-unittest-same.dmp"; | ||||
|     ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit, | ||||
|                               child_pid, NULL, 0, | ||||
|                               MappingList(), AppMemoryList())); | ||||
|     struct stat st; | ||||
|     ASSERT_EQ(0, stat(same_dump.c_str(), &st)); | ||||
|     // Make sure limiting wasn't actually triggered.  NOTE: If you fail this, | ||||
|     // first make sure that "minidump_size_limit" above is indeed set to a | ||||
|     // large enough value -- the limit-checking code in minidump_writer.cc | ||||
|     // does just a rough estimate. | ||||
|     ASSERT_EQ(normal_file_size, st.st_size); | ||||
|   } | ||||
|  | ||||
|   // Third, write a minidump with a size limit small enough to be triggered. | ||||
|   { | ||||
|     // Set size limit to some arbitrary amount, such that the limiting code | ||||
|     // will kick in.  The equation used to set this value was determined by | ||||
|     // simply reversing the size-limit logic a little bit in order to pick a | ||||
|     // size we know will trigger it.  The definition of | ||||
|     // kLimitAverageThreadStackLength here was copied from class | ||||
|     // MinidumpWriter in minidump_writer.cc. | ||||
|     static const unsigned kLimitAverageThreadStackLength = 8 * 1024; | ||||
|     off_t minidump_size_limit = kNumberOfThreadsInHelperProgram * | ||||
|         kLimitAverageThreadStackLength; | ||||
|     // If, in reality, each of the threads' stack is *smaller* than | ||||
|     // kLimitAverageThreadStackLength, the normal file size could very well be | ||||
|     // smaller than the arbitrary limit that was just set.  In that case, | ||||
|     // either of these numbers should trigger the size-limiting code, but we | ||||
|     // might as well pick the smallest. | ||||
|     if (normal_file_size < minidump_size_limit) | ||||
|       minidump_size_limit = normal_file_size; | ||||
|  | ||||
|     string limit_dump = temp_dir.path() + | ||||
|         "/minidump-writer-unittest-limit.dmp"; | ||||
|     ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit, | ||||
|                               child_pid, NULL, 0, | ||||
|                               MappingList(), AppMemoryList())); | ||||
|     struct stat st; | ||||
|     ASSERT_EQ(0, stat(limit_dump.c_str(), &st)); | ||||
|     ASSERT_GT(st.st_size, 0); | ||||
|     // Make sure the file size is at least smaller than the original.  If this | ||||
|     // fails because it's the same size, then the size-limit logic didn't kick | ||||
|     // in like it was supposed to. | ||||
|     EXPECT_LT(st.st_size, normal_file_size); | ||||
|  | ||||
|     Minidump minidump(limit_dump); | ||||
|     ASSERT_TRUE(minidump.Read()); | ||||
|     MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); | ||||
|     ASSERT_TRUE(dump_thread_list); | ||||
|     int total_limit_stack_size = 0; | ||||
|     for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { | ||||
|       MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); | ||||
|       ASSERT_TRUE(thread->thread() != NULL); | ||||
|       // When the stack size is zero bytes, GetMemory() returns NULL. | ||||
|       MinidumpMemoryRegion* memory = thread->GetMemory(); | ||||
|       ASSERT_TRUE(memory != NULL); | ||||
|       total_limit_stack_size += memory->GetSize(); | ||||
|     } | ||||
|  | ||||
|     // Make sure stack size shrunk by at least 1KB per extra thread.  The | ||||
|     // definition of kLimitBaseThreadCount here was copied from class | ||||
|     // MinidumpWriter in minidump_writer.cc. | ||||
|     // Note: The 1KB is arbitrary, and assumes that the thread stacks are big | ||||
|     // enough to shrink by that much.  For example, if each thread stack was | ||||
|     // originally only 2KB, the current size-limit logic wouldn't actually | ||||
|     // shrink them because that's the size to which it tries to shrink.  If | ||||
|     // you fail this part of the test due to something like that, the test | ||||
|     // logic should probably be improved to account for your situation. | ||||
|     const unsigned kLimitBaseThreadCount = 20; | ||||
|     const unsigned kMinPerExtraThreadStackReduction = 1024; | ||||
|     const int min_expected_reduction = (kNumberOfThreadsInHelperProgram - | ||||
|         kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction; | ||||
|     EXPECT_LT(total_limit_stack_size, | ||||
|               total_normal_stack_size - min_expected_reduction); | ||||
|   } | ||||
|  | ||||
|   // Kill the helper program. | ||||
|   kill(child_pid, SIGKILL); | ||||
| } | ||||
|  | ||||
| }  // namespace | ||||
| @@ -0,0 +1,66 @@ | ||||
| // Copyright (c) 2011 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // minidump_writer_unittest_utils.cc: | ||||
| // Shared routines used by unittests under client/linux/minidump_writer. | ||||
|  | ||||
| #include <limits.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" | ||||
| #include "common/linux/safe_readlink.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| string GetHelperBinary() { | ||||
|   string helper_path; | ||||
|   char *bindir = getenv("bindir"); | ||||
|   if (bindir) { | ||||
|     helper_path = string(bindir) + "/"; | ||||
|   } else { | ||||
|     // Locate helper binary next to the current binary. | ||||
|     char self_path[PATH_MAX]; | ||||
|     if (!SafeReadLink("/proc/self/exe", self_path)) { | ||||
|       return ""; | ||||
|     } | ||||
|     helper_path = string(self_path); | ||||
|     size_t pos = helper_path.rfind('/'); | ||||
|     if (pos == string::npos) { | ||||
|       return ""; | ||||
|     } | ||||
|     helper_path.erase(pos + 1); | ||||
|   } | ||||
|  | ||||
|   helper_path += "linux_dumper_unittest_helper"; | ||||
|  | ||||
|   return helper_path; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
| @@ -0,0 +1,49 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // minidump_writer_unittest_utils.h: | ||||
| // Shared routines used by unittests under client/linux/minidump_writer. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // Returns the full path to linux_dumper_unittest_helper.  The full path is | ||||
| // discovered either by using the environment variable "bindir" or by using | ||||
| // the location of the main module of the currently running process. | ||||
| string GetHelperBinary(); | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ | ||||
							
								
								
									
										130
									
								
								sdk/breakpad/client/linux/minidump_writer/proc_cpuinfo_reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								sdk/breakpad/client/linux/minidump_writer/proc_cpuinfo_reader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| // Copyright (c) 2013, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ | ||||
| #define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ | ||||
|  | ||||
| #include <stdint.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "client/linux/minidump_writer/line_reader.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // A class for reading /proc/cpuinfo without using fopen/fgets or other | ||||
| // functions which may allocate memory. | ||||
| class ProcCpuInfoReader { | ||||
| public: | ||||
|   ProcCpuInfoReader(int fd) | ||||
|     : line_reader_(fd), pop_count_(-1) { | ||||
|   } | ||||
|  | ||||
|   // Return the next field name, or NULL in case of EOF. | ||||
|   // field: (output) Pointer to zero-terminated field name. | ||||
|   // Returns true on success, or false on EOF or error (line too long). | ||||
|   bool GetNextField(const char** field) { | ||||
|     for (;;) { | ||||
|       const char* line; | ||||
|       unsigned line_len; | ||||
|  | ||||
|       // Try to read next line. | ||||
|       if (pop_count_ >= 0) { | ||||
|         line_reader_.PopLine(pop_count_); | ||||
|         pop_count_ = -1; | ||||
|       } | ||||
|  | ||||
|       if (!line_reader_.GetNextLine(&line, &line_len)) | ||||
|         return false; | ||||
|  | ||||
|       pop_count_ = static_cast<int>(line_len); | ||||
|  | ||||
|       const char* line_end = line + line_len; | ||||
|  | ||||
|       // Expected format: <field-name> <space>+ ':' <space> <value> | ||||
|       // Note that: | ||||
|       //   - empty lines happen. | ||||
|       //   - <field-name> can contain spaces. | ||||
|       //   - some fields have an empty <value> | ||||
|       char* sep = static_cast<char*>(my_memchr(line, ':', line_len)); | ||||
|       if (sep == NULL) | ||||
|         continue; | ||||
|  | ||||
|       // Record the value. Skip leading space after the column to get | ||||
|       // its start. | ||||
|       const char* val = sep+1; | ||||
|       while (val < line_end && my_isspace(*val)) | ||||
|         val++; | ||||
|  | ||||
|       value_ = val; | ||||
|       value_len_ = static_cast<size_t>(line_end - val); | ||||
|  | ||||
|       // Remove trailing spaces before the column to properly 0-terminate | ||||
|       // the field name. | ||||
|       while (sep > line && my_isspace(sep[-1])) | ||||
|         sep--; | ||||
|  | ||||
|       if (sep == line) | ||||
|         continue; | ||||
|  | ||||
|       // zero-terminate field name. | ||||
|       *sep = '\0'; | ||||
|  | ||||
|       *field = line; | ||||
|       return true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Return the field value. This must be called after a succesful | ||||
|   // call to GetNextField(). | ||||
|   const char* GetValue() { | ||||
|     assert(value_); | ||||
|     return value_; | ||||
|   } | ||||
|  | ||||
|   // Same as GetValue(), but also returns the length in characters of | ||||
|   // the value. | ||||
|   const char* GetValueAndLen(size_t* length) { | ||||
|     assert(value_); | ||||
|     *length = value_len_; | ||||
|     return value_; | ||||
|   } | ||||
|  | ||||
| private: | ||||
|   LineReader line_reader_; | ||||
|   int pop_count_; | ||||
|   const char* value_; | ||||
|   size_t value_len_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ | ||||
| @@ -0,0 +1,199 @@ | ||||
| // Copyright (c) 2013, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <sys/types.h> | ||||
| #include <stdio.h> | ||||
| #include <errno.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "client/linux/minidump_writer/proc_cpuinfo_reader.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/linux/tests/auto_testfile.h" | ||||
|  | ||||
| using namespace google_breakpad; | ||||
|  | ||||
| #if !defined(__ANDROID__) | ||||
| #define TEMPDIR "/tmp" | ||||
| #else | ||||
| #define TEMPDIR "/data/local/tmp" | ||||
| #endif | ||||
|  | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| typedef testing::Test ProcCpuInfoReaderTest; | ||||
|  | ||||
| class ScopedTestFile : public AutoTestFile { | ||||
| public: | ||||
|   explicit ScopedTestFile(const char* text) | ||||
|     : AutoTestFile("proc_cpuinfo_reader", text) { | ||||
|   } | ||||
| }; | ||||
|  | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, EmptyFile) { | ||||
|   ScopedTestFile file(""); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *field; | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, OneLineTerminated) { | ||||
|   ScopedTestFile file("foo : bar\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   ASSERT_STREQ("bar", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, OneLine) { | ||||
|   ScopedTestFile file("foo : bar"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char *field; | ||||
|   size_t value_len; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len)); | ||||
|   ASSERT_EQ(3U, value_len); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) { | ||||
|   ScopedTestFile file("foo : bar\nzoo : tut\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char* field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   ASSERT_STREQ("bar", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("zoo", field); | ||||
|   ASSERT_STREQ("tut", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, SkipMalformedLine) { | ||||
|   ScopedTestFile file("this line should have a column\nfoo : bar\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char* field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   ASSERT_STREQ("bar", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) { | ||||
|   ScopedTestFile file("\n\nfoo : bar\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char* field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   ASSERT_STREQ("bar", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, SkipEmptyField) { | ||||
|   ScopedTestFile file(" : bar\nzoo : tut\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char* field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("zoo", field); | ||||
|   ASSERT_STREQ("tut", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) { | ||||
|   ScopedTestFile file("foo : bar\n\n\nfoo : bar\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char* field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   ASSERT_STREQ("bar", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   ASSERT_STREQ("bar", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, FieldWithSpaces) { | ||||
|   ScopedTestFile file("foo bar    : zoo\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char* field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo bar", field); | ||||
|   ASSERT_STREQ("zoo", reader.GetValue()); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
|  | ||||
| TEST(ProcCpuInfoReaderTest, EmptyValue) { | ||||
|   ScopedTestFile file("foo :\n"); | ||||
|   ASSERT_TRUE(file.IsOk()); | ||||
|   ProcCpuInfoReader reader(file.GetFd()); | ||||
|  | ||||
|   const char* field; | ||||
|   ASSERT_TRUE(reader.GetNextField(&field)); | ||||
|   ASSERT_STREQ("foo", field); | ||||
|   size_t value_len; | ||||
|   ASSERT_STREQ("", reader.GetValueAndLen(&value_len)); | ||||
|   ASSERT_EQ(0U, value_len); | ||||
|  | ||||
|   ASSERT_FALSE(reader.GetNextField(&field)); | ||||
| } | ||||
							
								
								
									
										104
									
								
								sdk/breakpad/client/linux/sender/google_crash_report_sender.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								sdk/breakpad/client/linux/sender/google_crash_report_sender.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include "common/linux/google_crashdump_uploader.h" | ||||
| #include "third_party/linux/include/gflags/gflags.h" | ||||
| #include <string> | ||||
| #include <iostream> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| DEFINE_string(crash_server, "https://clients2.google.com/cr", | ||||
|               "The crash server to upload minidumps to."); | ||||
| DEFINE_string(product_name, "", | ||||
|               "The product name that the minidump corresponds to."); | ||||
| DEFINE_string(product_version, "", | ||||
|               "The version of the product that produced the minidump."); | ||||
| DEFINE_string(client_id, "", | ||||
|               "The client GUID"); | ||||
| DEFINE_string(minidump_path, "", | ||||
|               "The path of the minidump file."); | ||||
| DEFINE_string(ptime, "", | ||||
|               "The process uptime in milliseconds."); | ||||
| DEFINE_string(ctime, "", | ||||
|               "The cumulative process uptime in milliseconds."); | ||||
| DEFINE_string(email, "", | ||||
|               "The user's email address."); | ||||
| DEFINE_string(comments, "", | ||||
|               "Extra user comments"); | ||||
| DEFINE_string(proxy_host, "", | ||||
|               "Proxy host"); | ||||
| DEFINE_string(proxy_userpasswd, "", | ||||
|               "Proxy username/password in user:pass format."); | ||||
|  | ||||
|  | ||||
| bool CheckForRequiredFlagsOrDie() { | ||||
|   string error_text = ""; | ||||
|   if (FLAGS_product_name.empty()) { | ||||
|     error_text.append("\nProduct name must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (FLAGS_product_version.empty()) { | ||||
|     error_text.append("\nProduct version must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (FLAGS_client_id.empty()) { | ||||
|     error_text.append("\nClient ID must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (FLAGS_minidump_path.empty()) { | ||||
|     error_text.append("\nMinidump pathname must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (!error_text.empty()) { | ||||
|     std::cout << error_text; | ||||
|     return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) { | ||||
|   google::InitGoogleLogging(argv[0]); | ||||
|   google::ParseCommandLineFlags(&argc, &argv, true); | ||||
|   if (!CheckForRequiredFlagsOrDie()) { | ||||
|     return 1; | ||||
|   } | ||||
|   google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, | ||||
|                                              FLAGS_product_version, | ||||
|                                              FLAGS_client_id, | ||||
|                                              FLAGS_ptime, | ||||
|                                              FLAGS_ctime, | ||||
|                                              FLAGS_email, | ||||
|                                              FLAGS_comments, | ||||
|                                              FLAGS_minidump_path, | ||||
|                                              FLAGS_crash_server, | ||||
|                                              FLAGS_proxy_host, | ||||
|                                              FLAGS_proxy_userpasswd); | ||||
|   g.Upload(); | ||||
| } | ||||
							
								
								
									
										97
									
								
								sdk/breakpad/client/minidump_file_writer-inl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								sdk/breakpad/client/minidump_file_writer-inl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | ||||
| // Copyright (c) 2006, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // minidump_file_writer-inl.h: Minidump file writer implementation. | ||||
| // | ||||
| // See minidump_file_writer.h for documentation. | ||||
|  | ||||
| #ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__ | ||||
| #define CLIENT_MINIDUMP_FILE_WRITER_INL_H__ | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| #include "client/minidump_file_writer.h" | ||||
| #include "google_breakpad/common/minidump_size.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| template<typename MDType> | ||||
| inline bool TypedMDRVA<MDType>::Allocate() { | ||||
|   allocation_state_ = SINGLE_OBJECT; | ||||
|   return UntypedMDRVA::Allocate(minidump_size<MDType>::size()); | ||||
| } | ||||
|  | ||||
| template<typename MDType> | ||||
| inline bool TypedMDRVA<MDType>::Allocate(size_t additional) { | ||||
|   allocation_state_ = SINGLE_OBJECT; | ||||
|   return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + additional); | ||||
| } | ||||
|  | ||||
| template<typename MDType> | ||||
| inline bool TypedMDRVA<MDType>::AllocateArray(size_t count) { | ||||
|   assert(count); | ||||
|   allocation_state_ = ARRAY; | ||||
|   return UntypedMDRVA::Allocate(minidump_size<MDType>::size() * count); | ||||
| } | ||||
|  | ||||
| template<typename MDType> | ||||
| inline bool TypedMDRVA<MDType>::AllocateObjectAndArray(size_t count, | ||||
|                                                        size_t length) { | ||||
|   assert(count && length); | ||||
|   allocation_state_ = SINGLE_OBJECT_WITH_ARRAY; | ||||
|   return UntypedMDRVA::Allocate(minidump_size<MDType>::size() + count * length); | ||||
| } | ||||
|  | ||||
| template<typename MDType> | ||||
| inline bool TypedMDRVA<MDType>::CopyIndex(unsigned int index, MDType *item) { | ||||
|   assert(allocation_state_ == ARRAY); | ||||
|   return writer_->Copy( | ||||
|       static_cast<MDRVA>(position_ + index * minidump_size<MDType>::size()),  | ||||
|       item, minidump_size<MDType>::size()); | ||||
| } | ||||
|  | ||||
| template<typename MDType> | ||||
| inline bool TypedMDRVA<MDType>::CopyIndexAfterObject(unsigned int index, | ||||
|                                                      const void *src,  | ||||
|                                                      size_t length) { | ||||
|   assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY); | ||||
|   return writer_->Copy( | ||||
|       static_cast<MDRVA>(position_ + minidump_size<MDType>::size()  | ||||
|                          + index * length), | ||||
|       src, length); | ||||
| } | ||||
|  | ||||
| template<typename MDType> | ||||
| inline bool TypedMDRVA<MDType>::Flush() { | ||||
|   return writer_->Copy(position_, &data_, minidump_size<MDType>::size()); | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_MINIDUMP_FILE_WRITER_INL_H__ | ||||
							
								
								
									
										284
									
								
								sdk/breakpad/client/minidump_file_writer.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										284
									
								
								sdk/breakpad/client/minidump_file_writer.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,284 @@ | ||||
| // 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 "client/minidump_file_writer-inl.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/string_conversion.h" | ||||
| #if __linux__ | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
| #endif | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast<MDRVA>(-1); | ||||
|  | ||||
| MinidumpFileWriter::MinidumpFileWriter() | ||||
|     : file_(-1), | ||||
|       close_file_when_destroyed_(true), | ||||
|       position_(0), | ||||
|       size_(0) { | ||||
| } | ||||
|  | ||||
| MinidumpFileWriter::~MinidumpFileWriter() { | ||||
|   if (close_file_when_destroyed_) | ||||
|     Close(); | ||||
| } | ||||
|  | ||||
| bool MinidumpFileWriter::Open(const char *path) { | ||||
|   assert(file_ == -1); | ||||
| #if __linux__ | ||||
|   file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); | ||||
| #else | ||||
|   file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); | ||||
| #endif | ||||
|  | ||||
|   return file_ != -1; | ||||
| } | ||||
|  | ||||
| void MinidumpFileWriter::SetFile(const int file) { | ||||
|   assert(file_ == -1); | ||||
|   file_ = file; | ||||
|   close_file_when_destroyed_ = false; | ||||
| } | ||||
|  | ||||
| bool MinidumpFileWriter::Close() { | ||||
|   bool result = true; | ||||
|  | ||||
|   if (file_ != -1) { | ||||
|     if (-1 == ftruncate(file_, position_)) { | ||||
|        return false; | ||||
|     } | ||||
| #if __linux__ | ||||
|     result = (sys_close(file_) == 0); | ||||
| #else | ||||
|     result = (close(file_) == 0); | ||||
| #endif | ||||
|     file_ = -1; | ||||
|   } | ||||
|  | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| bool MinidumpFileWriter::CopyStringToMDString(const wchar_t *str, | ||||
|                                               unsigned int length, | ||||
|                                               TypedMDRVA<MDString> *mdstring) { | ||||
|   bool result = true; | ||||
|   if (sizeof(wchar_t) == sizeof(uint16_t)) { | ||||
|     // Shortcut if wchar_t is the same size as MDString's buffer | ||||
|     result = mdstring->Copy(str, mdstring->get()->length); | ||||
|   } else { | ||||
|     uint16_t out[2]; | ||||
|     int out_idx = 0; | ||||
|  | ||||
|     // Copy the string character by character | ||||
|     while (length && result) { | ||||
|       UTF32ToUTF16Char(*str, out); | ||||
|       if (!out[0]) | ||||
|         return false; | ||||
|  | ||||
|       // Process one character at a time | ||||
|       --length; | ||||
|       ++str; | ||||
|  | ||||
|       // Append the one or two UTF-16 characters.  The first one will be non- | ||||
|       // zero, but the second one may be zero, depending on the conversion from | ||||
|       // UTF-32. | ||||
|       int out_count = out[1] ? 2 : 1; | ||||
|       size_t out_size = sizeof(uint16_t) * out_count; | ||||
|       result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); | ||||
|       out_idx += out_count; | ||||
|     } | ||||
|   } | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| bool MinidumpFileWriter::CopyStringToMDString(const char *str, | ||||
|                                               unsigned int length, | ||||
|                                               TypedMDRVA<MDString> *mdstring) { | ||||
|   bool result = true; | ||||
|   uint16_t out[2]; | ||||
|   int out_idx = 0; | ||||
|  | ||||
|   // Copy the string character by character | ||||
|   while (length && result) { | ||||
|     int conversion_count = UTF8ToUTF16Char(str, length, out); | ||||
|     if (!conversion_count) | ||||
|       return false; | ||||
|  | ||||
|     // Move the pointer along based on the nubmer of converted characters | ||||
|     length -= conversion_count; | ||||
|     str += conversion_count; | ||||
|  | ||||
|     // Append the one or two UTF-16 characters | ||||
|     int out_count = out[1] ? 2 : 1; | ||||
|     size_t out_size = sizeof(uint16_t) * out_count; | ||||
|     result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); | ||||
|     out_idx += out_count; | ||||
|   } | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| template <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(uint16_t))) | ||||
|     return false; | ||||
|  | ||||
|   // Set length excluding the NULL and copy the string | ||||
|   mdstring.get()->length = | ||||
|       static_cast<uint32_t>(mdstring_length * sizeof(uint16_t)); | ||||
|   bool result = CopyStringToMDString(str, mdstring_length, &mdstring); | ||||
|  | ||||
|   // NULL terminate | ||||
|   if (result) { | ||||
|     uint16_t ch = 0; | ||||
|     result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch)); | ||||
|  | ||||
|     if (result) | ||||
|       *location = mdstring.location(); | ||||
|   } | ||||
|  | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| bool MinidumpFileWriter::WriteString(const wchar_t *str, unsigned int length, | ||||
|                  MDLocationDescriptor *location) { | ||||
|   return WriteStringCore(str, length, location); | ||||
| } | ||||
|  | ||||
| bool MinidumpFileWriter::WriteString(const char *str, unsigned int length, | ||||
|                  MDLocationDescriptor *location) { | ||||
|   return WriteStringCore(str, length, location); | ||||
| } | ||||
|  | ||||
| bool MinidumpFileWriter::WriteMemory(const void *src, size_t size, | ||||
|                                      MDMemoryDescriptor *output) { | ||||
|   assert(src); | ||||
|   assert(output); | ||||
|   UntypedMDRVA mem(this); | ||||
|  | ||||
|   if (!mem.Allocate(size)) | ||||
|     return false; | ||||
|   if (!mem.Copy(src, mem.size())) | ||||
|     return false; | ||||
|  | ||||
|   output->start_of_memory_range = reinterpret_cast<uint64_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 (static_cast<size_t>(size + position) > size_) | ||||
|     return false; | ||||
|  | ||||
|   // Seek and write the data | ||||
| #if __linux__ | ||||
|   if (sys_lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) { | ||||
|     if (sys_write(file_, src, size) == size) { | ||||
| #else | ||||
|   if (lseek(file_, position, SEEK_SET) == static_cast<off_t>(position)) { | ||||
|     if (write(file_, src, size) == size) { | ||||
| #endif | ||||
|       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 pos, const void *src, size_t size) { | ||||
|   assert(src); | ||||
|   assert(size); | ||||
|   assert(pos + size <= position_ + size_); | ||||
|   return writer_->Copy(pos, src, size); | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										272
									
								
								sdk/breakpad/client/minidump_file_writer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										272
									
								
								sdk/breakpad/client/minidump_file_writer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,272 @@ | ||||
| // Copyright (c) 2006, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // minidump_file_writer.h:  Implements file-based minidump generation.  It's | ||||
| // intended to be used with the Google Breakpad open source crash handling | ||||
| // project. | ||||
|  | ||||
| #ifndef CLIENT_MINIDUMP_FILE_WRITER_H__ | ||||
| #define CLIENT_MINIDUMP_FILE_WRITER_H__ | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "google_breakpad/common/minidump_format.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class UntypedMDRVA; | ||||
| template<typename MDType> class TypedMDRVA; | ||||
|  | ||||
| // The user of this class can Open() a file and add minidump streams, data, and | ||||
| // strings using the definitions in minidump_format.h.  Since this class is | ||||
| // expected to be used in a situation where the current process may be | ||||
| // damaged, it will not allocate heap memory. | ||||
| // Sample usage: | ||||
| // MinidumpFileWriter writer; | ||||
| // writer.Open("/tmp/minidump.dmp"); | ||||
| // TypedMDRVA<MDRawHeader> header(&writer_); | ||||
| // header.Allocate(); | ||||
| // header->get()->signature = MD_HEADER_SIGNATURE; | ||||
| //  : | ||||
| // writer.Close(); | ||||
| // | ||||
| // An alternative is to use SetFile and provide a file descriptor: | ||||
| // MinidumpFileWriter writer; | ||||
| // writer.SetFile(minidump_fd); | ||||
| // TypedMDRVA<MDRawHeader> header(&writer_); | ||||
| // header.Allocate(); | ||||
| // header->get()->signature = MD_HEADER_SIGNATURE; | ||||
| //  : | ||||
| // writer.Close(); | ||||
|  | ||||
| class MinidumpFileWriter { | ||||
| public: | ||||
|   // Invalid MDRVA (Minidump Relative Virtual Address) | ||||
|   // returned on failed allocation | ||||
|   static const MDRVA kInvalidMDRVA; | ||||
|  | ||||
|   MinidumpFileWriter(); | ||||
|   ~MinidumpFileWriter(); | ||||
|  | ||||
|   // Open |path| as the destination of the minidump data.  Any existing file | ||||
|   // will be overwritten. | ||||
|   // Return true on success, or false on failure. | ||||
|   bool Open(const char *path); | ||||
|  | ||||
|   // Sets the file descriptor |file| as the destination of the minidump data. | ||||
|   // Can be used as an alternative to Open() when a file descriptor is | ||||
|   // available. | ||||
|   // Note that |fd| is not closed when the instance of MinidumpFileWriter is | ||||
|   // destroyed. | ||||
|   void SetFile(const int file); | ||||
|  | ||||
|   // Close the current file (that was either created when Open was called, or | ||||
|   // specified with SetFile). | ||||
|   // Return true on success, or false on failure. | ||||
|   bool Close(); | ||||
|  | ||||
|   // Copy the contents of |str| to a MDString and write it to the file. | ||||
|   // |str| is expected to be either UTF-16 or UTF-32 depending on the size | ||||
|   // of wchar_t. | ||||
|   // Maximum |length| of characters to copy from |str|, or specify 0 to use the | ||||
|   // entire NULL terminated string.  Copying will stop at the first NULL. | ||||
|   // |location| the allocated location | ||||
|   // Return true on success, or false on failure | ||||
|   bool WriteString(const wchar_t *str, unsigned int length, | ||||
|                    MDLocationDescriptor *location); | ||||
|  | ||||
|   // Same as above, except with |str| as a UTF-8 string | ||||
|   bool WriteString(const char *str, unsigned int length, | ||||
|                    MDLocationDescriptor *location); | ||||
|  | ||||
|   // Write |size| bytes starting at |src| into the current position. | ||||
|   // Return true on success and set |output| to position, or false on failure | ||||
|   bool WriteMemory(const void *src, size_t size, MDMemoryDescriptor *output); | ||||
|  | ||||
|   // Copies |size| bytes from |src| to |position| | ||||
|   // Return true on success, or false on failure | ||||
|   bool Copy(MDRVA position, const void *src, ssize_t size); | ||||
|  | ||||
|   // Return the current position for writing to the minidump | ||||
|   inline MDRVA position() const { return position_; } | ||||
|  | ||||
|  private: | ||||
|   friend class UntypedMDRVA; | ||||
|  | ||||
|   // Allocates an area of |size| bytes. | ||||
|   // Returns the position of the allocation, or kInvalidMDRVA if it was | ||||
|   // unable to allocate the bytes. | ||||
|   MDRVA Allocate(size_t size); | ||||
|  | ||||
|   // The file descriptor for the output file. | ||||
|   int file_; | ||||
|  | ||||
|   // Whether |file_| should be closed when the instance is destroyed. | ||||
|   bool close_file_when_destroyed_; | ||||
|  | ||||
|   // Current position in buffer | ||||
|   MDRVA position_; | ||||
|  | ||||
|   // Current allocated size | ||||
|   size_t size_; | ||||
|  | ||||
|   // Copy |length| characters from |str| to |mdstring|.  These are distinct | ||||
|   // because the underlying MDString is a UTF-16 based string.  The wchar_t | ||||
|   // variant may need to create a MDString that has more characters than the | ||||
|   // source |str|, whereas the UTF-8 variant may coalesce characters to form | ||||
|   // a single UTF-16 character. | ||||
|   bool CopyStringToMDString(const wchar_t *str, unsigned int length, | ||||
|                             TypedMDRVA<MDString> *mdstring); | ||||
|   bool CopyStringToMDString(const char *str, unsigned int length, | ||||
|                             TypedMDRVA<MDString> *mdstring); | ||||
|  | ||||
|   // The common templated code for writing a string | ||||
|   template <typename CharType> | ||||
|   bool WriteStringCore(const CharType *str, unsigned int length, | ||||
|                        MDLocationDescriptor *location); | ||||
| }; | ||||
|  | ||||
| // Represents an untyped allocated chunk | ||||
| class UntypedMDRVA { | ||||
|  public: | ||||
|   explicit UntypedMDRVA(MinidumpFileWriter *writer) | ||||
|       : writer_(writer), | ||||
|         position_(writer->position()), | ||||
|         size_(0) {} | ||||
|  | ||||
|   // Allocates |size| bytes.  Must not call more than once. | ||||
|   // Return true on success, or false on failure | ||||
|   bool Allocate(size_t size); | ||||
|  | ||||
|   // Returns the current position or kInvalidMDRVA if allocation failed | ||||
|   inline MDRVA position() const { return position_; } | ||||
|  | ||||
|   // Number of bytes allocated | ||||
|   inline size_t size() const { return size_; } | ||||
|  | ||||
|   // Return size and position | ||||
|   inline MDLocationDescriptor location() const { | ||||
|     MDLocationDescriptor location = { static_cast<uint32_t>(size_), | ||||
|                                       position_ }; | ||||
|     return location; | ||||
|   } | ||||
|  | ||||
|   // Copy |size| bytes starting at |src| into the minidump at |position| | ||||
|   // Return true on success, or false on failure | ||||
|   bool Copy(MDRVA position, const void *src, size_t size); | ||||
|  | ||||
|   // Copy |size| bytes from |src| to the current position | ||||
|   inline bool Copy(const void *src, size_t size) { | ||||
|     return Copy(position_, src, size); | ||||
|   } | ||||
|  | ||||
|  protected: | ||||
|   // Writer we associate with | ||||
|   MinidumpFileWriter *writer_; | ||||
|  | ||||
|   // Position of the start of the data | ||||
|   MDRVA position_; | ||||
|  | ||||
|   // Allocated size | ||||
|   size_t size_; | ||||
| }; | ||||
|  | ||||
| // Represents a Minidump object chunk.  Additional memory can be allocated at | ||||
| // the end of the object as a: | ||||
| // - single allocation | ||||
| // - Array of MDType objects | ||||
| // - A MDType object followed by an array | ||||
| template<typename MDType> | ||||
| class TypedMDRVA : public UntypedMDRVA { | ||||
|  public: | ||||
|   // Constructs an unallocated MDRVA | ||||
|   explicit TypedMDRVA(MinidumpFileWriter *writer) | ||||
|       : UntypedMDRVA(writer), | ||||
|         data_(), | ||||
|         allocation_state_(UNALLOCATED) {} | ||||
|  | ||||
|   inline ~TypedMDRVA() { | ||||
|     // Ensure that the data_ object is written out | ||||
|     if (allocation_state_ != ARRAY) | ||||
|       Flush(); | ||||
|   } | ||||
|  | ||||
|   // Address of object data_ of MDType.  This is not declared const as the | ||||
|   // typical usage will be to access the underlying |data_| object as to | ||||
|   // alter its contents. | ||||
|   MDType *get() { return &data_; } | ||||
|  | ||||
|   // Allocates minidump_size<MDType>::size() bytes. | ||||
|   // Must not call more than once. | ||||
|   // Return true on success, or false on failure | ||||
|   bool Allocate(); | ||||
|  | ||||
|   // Allocates minidump_size<MDType>::size() + |additional| bytes. | ||||
|   // Must not call more than once. | ||||
|   // Return true on success, or false on failure | ||||
|   bool Allocate(size_t additional); | ||||
|  | ||||
|   // Allocate an array of |count| elements of MDType. | ||||
|   // Must not call more than once. | ||||
|   // Return true on success, or false on failure | ||||
|   bool AllocateArray(size_t count); | ||||
|  | ||||
|   // Allocate an array of |count| elements of |size| after object of MDType | ||||
|   // Must not call more than once. | ||||
|   // Return true on success, or false on failure | ||||
|   bool AllocateObjectAndArray(size_t count, size_t size); | ||||
|  | ||||
|   // Copy |item| to |index| | ||||
|   // Must have been allocated using AllocateArray(). | ||||
|   // Return true on success, or false on failure | ||||
|   bool CopyIndex(unsigned int index, MDType *item); | ||||
|  | ||||
|   // Copy |size| bytes starting at |str| to |index| | ||||
|   // Must have been allocated using AllocateObjectAndArray(). | ||||
|   // Return true on success, or false on failure | ||||
|   bool CopyIndexAfterObject(unsigned int index, const void *src, size_t size); | ||||
|  | ||||
|   // Write data_ | ||||
|   bool Flush(); | ||||
|  | ||||
|  private: | ||||
|   enum AllocationState { | ||||
|     UNALLOCATED = 0, | ||||
|     SINGLE_OBJECT, | ||||
|     ARRAY, | ||||
|     SINGLE_OBJECT_WITH_ARRAY | ||||
|   }; | ||||
|  | ||||
|   MDType data_; | ||||
|   AllocationState allocation_state_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // CLIENT_MINIDUMP_FILE_WRITER_H__ | ||||
							
								
								
									
										179
									
								
								sdk/breakpad/client/minidump_file_writer_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								sdk/breakpad/client/minidump_file_writer_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| // Copyright (c) 2006, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Author: waylonis@google.com (Dan Waylonis) | ||||
|  | ||||
| /* | ||||
|  g++ -I../ ../common/convert_UTF.c \ | ||||
|  ../common/string_conversion.cc \ | ||||
|  minidump_file_writer.cc \ | ||||
|  minidump_file_writer_unittest.cc \ | ||||
|  -o minidump_file_writer_unittest | ||||
|  */ | ||||
|  | ||||
| #include <fcntl.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include "minidump_file_writer-inl.h" | ||||
|  | ||||
| using google_breakpad::MinidumpFileWriter; | ||||
|  | ||||
| #define ASSERT_TRUE(cond) \ | ||||
| if (!(cond)) { \ | ||||
|   fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ | ||||
|     return false; \ | ||||
| } | ||||
|  | ||||
| #define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) | ||||
| #define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2)) | ||||
|  | ||||
| struct StringStructure { | ||||
|   unsigned long integer_value; | ||||
|   MDLocationDescriptor first_string; | ||||
|   MDLocationDescriptor second_string; | ||||
| }; | ||||
|  | ||||
| struct ArrayStructure { | ||||
|   unsigned char char_value; | ||||
|   unsigned short short_value; | ||||
|   unsigned long long_value; | ||||
| }; | ||||
|  | ||||
| typedef struct { | ||||
|   unsigned long count; | ||||
|   ArrayStructure array[0]; | ||||
| } ObjectAndArrayStructure; | ||||
|  | ||||
| static bool WriteFile(const char *path) { | ||||
|   MinidumpFileWriter writer; | ||||
|   if (writer.Open(path)) { | ||||
|     // Test a single structure | ||||
|     google_breakpad::TypedMDRVA<StringStructure> strings(&writer); | ||||
|     ASSERT_TRUE(strings.Allocate()); | ||||
|     strings.get()->integer_value = 0xBEEF; | ||||
|     const char *first = "First String"; | ||||
|     ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string)); | ||||
|     const wchar_t *second = L"Second String"; | ||||
|     ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string)); | ||||
|  | ||||
|     // Test an array structure | ||||
|     google_breakpad::TypedMDRVA<ArrayStructure> array(&writer); | ||||
|     unsigned int count = 10; | ||||
|     ASSERT_TRUE(array.AllocateArray(count)); | ||||
|     for (unsigned char i = 0; i < count; ++i) { | ||||
|       ArrayStructure local; | ||||
|       local.char_value = i; | ||||
|       local.short_value = i + 1; | ||||
|       local.long_value = i + 2; | ||||
|       ASSERT_TRUE(array.CopyIndex(i, &local)); | ||||
|     } | ||||
|  | ||||
|     // Test an object followed by an array | ||||
|     google_breakpad::TypedMDRVA<ObjectAndArrayStructure> obj_array(&writer); | ||||
|     ASSERT_TRUE(obj_array.AllocateObjectAndArray(count, | ||||
|                                                  sizeof(ArrayStructure))); | ||||
|     obj_array.get()->count = count; | ||||
|     for (unsigned char i = 0; i < count; ++i) { | ||||
|       ArrayStructure local; | ||||
|       local.char_value = i; | ||||
|       local.short_value = i + 1; | ||||
|       local.long_value = i + 2; | ||||
|       ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local))); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return writer.Close(); | ||||
| } | ||||
|  | ||||
| static bool CompareFile(const char *path) { | ||||
|   unsigned long expected[] = { | ||||
| #if defined(__BIG_ENDIAN__) | ||||
|     0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000,  | ||||
|     0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069, | ||||
|     0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064, | ||||
|     0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002, | ||||
|     0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005, | ||||
|     0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008, | ||||
|     0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b, | ||||
|     0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003, | ||||
|     0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006, | ||||
|     0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009, | ||||
|     0x0000000a, 0x0900000a, 0x0000000b, 0x00000000 | ||||
| #else | ||||
|     0x0000beef, 0x0000001e, 0x00000018, 0x00000020, | ||||
|     0x00000038, 0x00000000, 0x00000018, 0x00690046, | ||||
|     0x00730072, 0x00200074, 0x00740053, 0x00690072, | ||||
|     0x0067006e, 0x00000000, 0x0000001a, 0x00650053, | ||||
|     0x006f0063, 0x0064006e, 0x00530020, 0x00720074, | ||||
|     0x006e0069, 0x00000067, 0x00011e00, 0x00000002, | ||||
|     0x00021e01, 0x00000003, 0x00031e02, 0x00000004, | ||||
|     0x00041e03, 0x00000005, 0x00051e04, 0x00000006, | ||||
|     0x00061e05, 0x00000007, 0x00071e06, 0x00000008, | ||||
|     0x00081e07, 0x00000009, 0x00091e08, 0x0000000a, | ||||
|     0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00, | ||||
|     0x00000002, 0x00021c01, 0x00000003, 0x00031c02, | ||||
|     0x00000004, 0x00041c03, 0x00000005, 0x00051c04, | ||||
|     0x00000006, 0x00061c05, 0x00000007, 0x00071c06, | ||||
|     0x00000008, 0x00081c07, 0x00000009, 0x00091c08, | ||||
|     0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000, | ||||
| #endif | ||||
|   }; | ||||
|   size_t expected_byte_count = sizeof(expected); | ||||
|   int fd = open(path, O_RDONLY, 0600); | ||||
|   void *buffer = malloc(expected_byte_count); | ||||
|   ASSERT_NE(fd, -1); | ||||
|   ASSERT_TRUE(buffer); | ||||
|   ASSERT_EQ(read(fd, buffer, expected_byte_count),  | ||||
|             static_cast<ssize_t>(expected_byte_count)); | ||||
|  | ||||
|   char *b1, *b2; | ||||
|   b1 = reinterpret_cast<char*>(buffer); | ||||
|   b2 = reinterpret_cast<char*>(expected); | ||||
|   while (*b1 == *b2) { | ||||
|     b1++; | ||||
|     b2++; | ||||
|   } | ||||
|  | ||||
|   printf("%p\n", reinterpret_cast<void*>(b1 - (char*)buffer)); | ||||
|  | ||||
|   ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| static bool RunTests() { | ||||
|   const char *path = "/tmp/minidump_file_writer_unittest.dmp"; | ||||
|   ASSERT_TRUE(WriteFile(path)); | ||||
|   ASSERT_TRUE(CompareFile(path)); | ||||
|   unlink(path); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| extern "C" int main(int argc, const char *argv[]) { | ||||
|   return RunTests() ? 0 : 1; | ||||
| } | ||||
							
								
								
									
										58
									
								
								sdk/breakpad/common/basictypes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								sdk/breakpad/common/basictypes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| // Copyright (c) 2011 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef COMMON_BASICTYPES_H_ | ||||
| #define COMMON_BASICTYPES_H_ | ||||
|  | ||||
| // A macro to disallow the copy constructor and operator= functions | ||||
| // This should be used in the private: declarations for a class | ||||
| #ifndef DISALLOW_COPY_AND_ASSIGN | ||||
| #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ | ||||
|   TypeName(const TypeName&);               \ | ||||
|   void operator=(const TypeName&) | ||||
| #endif  // DISALLOW_COPY_AND_ASSIGN | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // Used to explicitly mark the return value of a function as unused. If you are | ||||
| // really sure you don't want to do anything with the return value of a function | ||||
| // that has been marked with __attribute__((warn_unused_result)), wrap it with | ||||
| // this. Example: | ||||
| // | ||||
| //   scoped_ptr<MyType> my_var = ...; | ||||
| //   if (TakeOwnership(my_var.get()) == SUCCESS) | ||||
| //     ignore_result(my_var.release()); | ||||
| // | ||||
| template<typename T> | ||||
| inline void ignore_result(const T&) { | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_BASICTYPES_H_ | ||||
							
								
								
									
										265
									
								
								sdk/breakpad/common/byte_cursor.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								sdk/breakpad/common/byte_cursor.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,265 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // byte_cursor.h: Classes for parsing values from a buffer of bytes. | ||||
| // The ByteCursor class provides a convenient interface for reading | ||||
| // fixed-size integers of arbitrary endianness, being thorough about | ||||
| // checking for buffer overruns. | ||||
|  | ||||
| #ifndef COMMON_BYTE_CURSOR_H_ | ||||
| #define COMMON_BYTE_CURSOR_H_ | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdint.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <string> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // A buffer holding a series of bytes. | ||||
| struct ByteBuffer { | ||||
|   ByteBuffer() : start(0), end(0) { } | ||||
|   ByteBuffer(const uint8_t *set_start, size_t set_size) | ||||
|       : start(set_start), end(set_start + set_size) { } | ||||
|   ~ByteBuffer() { }; | ||||
|  | ||||
|   // Equality operators. Useful in unit tests, and when we're using | ||||
|   // ByteBuffers to refer to regions of a larger buffer. | ||||
|   bool operator==(const ByteBuffer &that) const { | ||||
|     return start == that.start && end == that.end; | ||||
|   } | ||||
|   bool operator!=(const ByteBuffer &that) const { | ||||
|     return start != that.start || end != that.end; | ||||
|   } | ||||
|  | ||||
|   // Not C++ style guide compliant, but this definitely belongs here. | ||||
|   size_t Size() const { | ||||
|     assert(start <= end); | ||||
|     return end - start; | ||||
|   } | ||||
|  | ||||
|   const uint8_t *start, *end; | ||||
| }; | ||||
|  | ||||
| // A cursor pointing into a ByteBuffer that can parse numbers of various | ||||
| // widths and representations, strings, and data blocks, advancing through | ||||
| // the buffer as it goes. All ByteCursor operations check that accesses | ||||
| // haven't gone beyond the end of the enclosing ByteBuffer. | ||||
| class ByteCursor { | ||||
|  public: | ||||
|   // Create a cursor reading bytes from the start of BUFFER. By default, the | ||||
|   // cursor reads multi-byte values in little-endian form. | ||||
|   ByteCursor(const ByteBuffer *buffer, bool big_endian = false) | ||||
|       : buffer_(buffer), here_(buffer->start),  | ||||
|         big_endian_(big_endian), complete_(true) { } | ||||
|  | ||||
|   // Accessor and setter for this cursor's endianness flag. | ||||
|   bool big_endian() const { return big_endian_; } | ||||
|   void set_big_endian(bool big_endian) { big_endian_ = big_endian; } | ||||
|  | ||||
|   // Accessor and setter for this cursor's current position. The setter | ||||
|   // returns a reference to this cursor. | ||||
|   const uint8_t *here() const { return here_; } | ||||
|   ByteCursor &set_here(const uint8_t *here) { | ||||
|     assert(buffer_->start <= here && here <= buffer_->end); | ||||
|     here_ = here; | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Return the number of bytes available to read at the cursor. | ||||
|   size_t Available() const { return size_t(buffer_->end - here_); } | ||||
|  | ||||
|   // Return true if this cursor is at the end of its buffer. | ||||
|   bool AtEnd() const { return Available() == 0; } | ||||
|  | ||||
|   // When used as a boolean value this cursor converts to true if all | ||||
|   // prior reads have been completed, or false if we ran off the end | ||||
|   // of the buffer. | ||||
|   operator bool() const { return complete_; } | ||||
|  | ||||
|   // Read a SIZE-byte integer at this cursor, signed if IS_SIGNED is true, | ||||
|   // unsigned otherwise, using the cursor's established endianness, and set | ||||
|   // *RESULT to the number. If we read off the end of our buffer, clear | ||||
|   // this cursor's complete_ flag, and store a dummy value in *RESULT. | ||||
|   // Return a reference to this cursor. | ||||
|   template<typename T> | ||||
|   ByteCursor &Read(size_t size, bool is_signed, T *result) { | ||||
|     if (CheckAvailable(size)) { | ||||
|       T v = 0; | ||||
|       if (big_endian_) { | ||||
|         for (size_t i = 0; i < size; i++) | ||||
|           v = (v << 8) + here_[i]; | ||||
|       } else { | ||||
|         // This loop condition looks weird, but size_t is unsigned, so | ||||
|         // decrementing i after it is zero yields the largest size_t value. | ||||
|         for (size_t i = size - 1; i < size; i--) | ||||
|           v = (v << 8) + here_[i]; | ||||
|       } | ||||
|       if (is_signed && size < sizeof(T)) { | ||||
|         size_t sign_bit = (T)1 << (size * 8 - 1); | ||||
|         v = (v ^ sign_bit) - sign_bit; | ||||
|       } | ||||
|       here_ += size; | ||||
|       *result = v; | ||||
|     } else { | ||||
|       *result = (T) 0xdeadbeef; | ||||
|     } | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Read an integer, using the cursor's established endianness and | ||||
|   // *RESULT's size and signedness, and set *RESULT to the number. If we | ||||
|   // read off the end of our buffer, clear this cursor's complete_ flag. | ||||
|   // Return a reference to this cursor. | ||||
|   template<typename T> | ||||
|   ByteCursor &operator>>(T &result) { | ||||
|     bool T_is_signed = (T)-1 < 0; | ||||
|     return Read(sizeof(T), T_is_signed, &result);  | ||||
|   } | ||||
|  | ||||
|   // Copy the SIZE bytes at the cursor to BUFFER, and advance this | ||||
|   // cursor to the end of them. If we read off the end of our buffer, | ||||
|   // clear this cursor's complete_ flag, and set *POINTER to NULL. | ||||
|   // Return a reference to this cursor. | ||||
|   ByteCursor &Read(uint8_t *buffer, size_t size) { | ||||
|     if (CheckAvailable(size)) { | ||||
|       memcpy(buffer, here_, size); | ||||
|       here_ += size; | ||||
|     } | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Set STR to a copy of the '\0'-terminated string at the cursor. If the | ||||
|   // byte buffer does not contain a terminating zero, clear this cursor's | ||||
|   // complete_ flag, and set STR to the empty string. Return a reference to | ||||
|   // this cursor. | ||||
|   ByteCursor &CString(string *str) { | ||||
|     const uint8_t *end | ||||
|       = static_cast<const uint8_t *>(memchr(here_, '\0', Available())); | ||||
|     if (end) { | ||||
|       str->assign(reinterpret_cast<const char *>(here_), end - here_); | ||||
|       here_ = end + 1; | ||||
|     } else { | ||||
|       str->clear(); | ||||
|       here_ = buffer_->end; | ||||
|       complete_ = false; | ||||
|     } | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Like CString(STR), but extract the string from a fixed-width buffer | ||||
|   // LIMIT bytes long, which may or may not contain a terminating '\0' | ||||
|   // byte. Specifically: | ||||
|   // | ||||
|   // - If there are not LIMIT bytes available at the cursor, clear the | ||||
|   //   cursor's complete_ flag and set STR to the empty string. | ||||
|   // | ||||
|   // - Otherwise, if the LIMIT bytes at the cursor contain any '\0' | ||||
|   //   characters, set *STR to a copy of the bytes before the first '\0', | ||||
|   //   and advance the cursor by LIMIT bytes. | ||||
|   //    | ||||
|   // - Otherwise, set *STR to a copy of those LIMIT bytes, and advance the | ||||
|   //   cursor by LIMIT bytes. | ||||
|   ByteCursor &CString(string *str, size_t limit) { | ||||
|     if (CheckAvailable(limit)) { | ||||
|       const uint8_t *end | ||||
|         = static_cast<const uint8_t *>(memchr(here_, '\0', limit)); | ||||
|       if (end) | ||||
|         str->assign(reinterpret_cast<const char *>(here_), end - here_); | ||||
|       else | ||||
|         str->assign(reinterpret_cast<const char *>(here_), limit); | ||||
|       here_ += limit; | ||||
|     } else { | ||||
|       str->clear(); | ||||
|     } | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Set *POINTER to point to the SIZE bytes at the cursor, and advance | ||||
|   // this cursor to the end of them. If SIZE is omitted, don't move the | ||||
|   // cursor. If we read off the end of our buffer, clear this cursor's | ||||
|   // complete_ flag, and set *POINTER to NULL. Return a reference to this | ||||
|   // cursor. | ||||
|   ByteCursor &PointTo(const uint8_t **pointer, size_t size = 0) { | ||||
|     if (CheckAvailable(size)) { | ||||
|       *pointer = here_; | ||||
|       here_ += size; | ||||
|     } else { | ||||
|       *pointer = NULL; | ||||
|     } | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Skip SIZE bytes at the cursor. If doing so would advance us off | ||||
|   // the end of our buffer, clear this cursor's complete_ flag, and | ||||
|   // set *POINTER to NULL. Return a reference to this cursor. | ||||
|   ByteCursor &Skip(size_t size) { | ||||
|     if (CheckAvailable(size)) | ||||
|       here_ += size; | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   // If there are at least SIZE bytes available to read from the buffer, | ||||
|   // return true. Otherwise, set here_ to the end of the buffer, set | ||||
|   // complete_ to false, and return false. | ||||
|   bool CheckAvailable(size_t size) { | ||||
|     if (Available() >= size) { | ||||
|       return true; | ||||
|     } else { | ||||
|       here_ = buffer_->end; | ||||
|       complete_ = false; | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // The buffer we're reading bytes from. | ||||
|   const ByteBuffer *buffer_; | ||||
|  | ||||
|   // The next byte within buffer_ that we'll read. | ||||
|   const uint8_t *here_; | ||||
|  | ||||
|   // True if we should read numbers in big-endian form; false if we | ||||
|   // should read in little-endian form. | ||||
|   bool big_endian_; | ||||
|  | ||||
|   // True if we've been able to read all we've been asked to. | ||||
|   bool complete_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_BYTE_CURSOR_H_ | ||||
							
								
								
									
										776
									
								
								sdk/breakpad/common/byte_cursor_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										776
									
								
								sdk/breakpad/common/byte_cursor_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,776 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer | ||||
| // and google_breakpad::ByteCursor. | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include <string.h> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/byte_cursor.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using google_breakpad::ByteBuffer; | ||||
| using google_breakpad::ByteCursor; | ||||
|  | ||||
| TEST(Buffer, SizeOfNothing) { | ||||
|   uint8_t data[1]; | ||||
|   ByteBuffer buffer(data, 0); | ||||
|   EXPECT_EQ(0U, buffer.Size()); | ||||
| } | ||||
|  | ||||
| TEST(Buffer, SizeOfSomething) { | ||||
|   uint8_t data[10]; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   EXPECT_EQ(10U, buffer.Size()); | ||||
| } | ||||
|  | ||||
| TEST(Extent, AvailableEmpty) { | ||||
|   uint8_t data[1]; | ||||
|   ByteBuffer buffer(data, 0); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   EXPECT_EQ(0U, cursor.Available()); | ||||
| } | ||||
|  | ||||
| TEST(Extent, AtEndEmpty) { | ||||
|   uint8_t data[1]; | ||||
|   ByteBuffer buffer(data, 0); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
| } | ||||
|  | ||||
| TEST(Extent, AsBoolEmpty) { | ||||
|   uint8_t data[1]; | ||||
|   ByteBuffer buffer(data, 0); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   EXPECT_TRUE(cursor); | ||||
| } | ||||
|  | ||||
| TEST(Extent, AvailableSome) { | ||||
|   uint8_t data[10]; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   EXPECT_EQ(10U, cursor.Available()); | ||||
| } | ||||
|  | ||||
| TEST(Extent, AtEndSome) { | ||||
|   uint8_t data[10]; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   EXPECT_FALSE(cursor.AtEnd()); | ||||
|   EXPECT_TRUE(cursor.Skip(sizeof(data)).AtEnd()); | ||||
| } | ||||
|  | ||||
| TEST(Extent, AsBoolSome) { | ||||
|   uint8_t data[10]; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   EXPECT_TRUE(cursor); | ||||
|   EXPECT_TRUE(cursor.Skip(sizeof(data))); | ||||
|   EXPECT_FALSE(cursor.Skip(1)); | ||||
| } | ||||
|  | ||||
| TEST(Extent, Cursor) { | ||||
|   uint8_t data[] = { 0xf7, | ||||
|                      0x9f, 0xbe, | ||||
|                      0x67, 0xfb, 0xd3, 0x58, | ||||
|                      0x6f, 0x36, 0xde, 0xd1, | ||||
|                      0x2a, 0x2a, 0x2a }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   uint8_t a; | ||||
|   uint16_t b; | ||||
|   uint32_t c; | ||||
|   uint32_t d; | ||||
|   uint8_t stars[3]; | ||||
|  | ||||
|   EXPECT_EQ(data + 0U, cursor.here()); | ||||
|  | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(data + 1U, cursor.here()); | ||||
|  | ||||
|   EXPECT_TRUE(cursor >> b); | ||||
|   EXPECT_EQ(data + 3U, cursor.here()); | ||||
|  | ||||
|   EXPECT_TRUE(cursor >> c); | ||||
|   EXPECT_EQ(data + 7U, cursor.here()); | ||||
|  | ||||
|   EXPECT_TRUE(cursor.Skip(4)); | ||||
|   EXPECT_EQ(data + 11U, cursor.here()); | ||||
|  | ||||
|   EXPECT_TRUE(cursor.Read(stars, 3)); | ||||
|   EXPECT_EQ(data + 14U, cursor.here()); | ||||
|  | ||||
|   EXPECT_FALSE(cursor >> d); | ||||
|   EXPECT_EQ(data + 14U, cursor.here()); | ||||
| } | ||||
|  | ||||
| TEST(Extent, SetOffset) { | ||||
|   uint8_t data[] = { 0x5c, 0x79, 0x8c, 0xd5 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   uint8_t a, b, c, d, e; | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(0x5cU, a); | ||||
|   EXPECT_EQ(data + 1U, cursor.here()); | ||||
|   EXPECT_TRUE(((cursor >> b).set_here(data + 3) >> c).set_here(data + 1) | ||||
|               >> d >> e); | ||||
|   EXPECT_EQ(0x79U, b); | ||||
|   EXPECT_EQ(0xd5U, c); | ||||
|   EXPECT_EQ(0x79U, d); | ||||
|   EXPECT_EQ(0x8cU, e); | ||||
|   EXPECT_EQ(data + 3U, cursor.here()); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Signed1) { | ||||
|   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   cursor.set_big_endian(true); | ||||
|   int a, b, c, d, e; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(1, true, &a) | ||||
|               .Read(1, true, &b) | ||||
|               .Read(1, true, &c) | ||||
|               .Read(1, true, &d)); | ||||
|   EXPECT_EQ(0,     a); | ||||
|   EXPECT_EQ(0x7f,  b); | ||||
|   EXPECT_EQ(-0x80, c); | ||||
|   EXPECT_EQ(-1,    d); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(1, true, &e)); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Signed2) { | ||||
|   uint8_t data[] = { 0x00, 0x00,   0x00, 0x80,   0x7f, 0xff, | ||||
|                      0x80, 0x00,   0x80, 0x80,   0xff, 0xff, | ||||
|                      0x39, 0xf1,   0x8a, 0xbc,   0x5a, 0xec }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer, true); | ||||
|   int a, b, c, d, e, f, g, h, i, j; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(2, true, &a) | ||||
|               .Read(2, true, &b) | ||||
|               .Read(2, true, &c) | ||||
|               .Read(2, true, &d) | ||||
|               .Read(2, true, &e) | ||||
|               .Read(2, true, &f) | ||||
|               .Read(2, true, &g) | ||||
|               .Read(2, true, &h) | ||||
|               .Read(2, true, &i)); | ||||
|   EXPECT_EQ(0,       a); | ||||
|   EXPECT_EQ(0x80,    b); | ||||
|   EXPECT_EQ(0x7fff,  c); | ||||
|   EXPECT_EQ(-0x8000, d); | ||||
|   EXPECT_EQ(-0x7f80, e); | ||||
|   EXPECT_EQ(-1,      f); | ||||
|   EXPECT_EQ(0x39f1,  g); | ||||
|   EXPECT_EQ(-0x7544, h); | ||||
|   EXPECT_EQ(0x5aec,  i); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(2, true, &j)); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Signed4) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||
|                      0x7f, 0xff, 0xff, 0xff, | ||||
|                      0x80, 0x00, 0x00, 0x00, | ||||
|                      0xff, 0xff, 0xff, 0xff, | ||||
|                      0xb6, 0xb1, 0xff, 0xef, | ||||
|                      0x19, 0x6a, 0xca, 0x46 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   cursor.set_big_endian(true); | ||||
|   int64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(4, true, &a) | ||||
|               .Read(4, true, &b) | ||||
|               .Read(4, true, &c) | ||||
|               .Read(4, true, &d) | ||||
|               .Read(4, true, &e) | ||||
|               .Read(4, true, &f)); | ||||
|   EXPECT_EQ(0,                    a); | ||||
|   EXPECT_EQ(0x7fffffff,           b); | ||||
|   EXPECT_EQ(-0x80000000LL,        c); | ||||
|   EXPECT_EQ(-1,                   d); | ||||
|   EXPECT_EQ((int32_t) 0xb6b1ffef, e); | ||||
|   EXPECT_EQ(0x196aca46,           f); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(4, true, &g)); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Signed8) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||
|                      0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||
|                      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||
|                      0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, | ||||
|                      0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer, true); | ||||
|   int64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(8, true, &a) | ||||
|               .Read(8, true, &b) | ||||
|               .Read(8, true, &c) | ||||
|               .Read(8, true, &d) | ||||
|               .Read(8, true, &e) | ||||
|               .Read(8, true, &f)); | ||||
|   EXPECT_EQ(0,                               a); | ||||
|   EXPECT_EQ(0x7fffffffffffffffLL,            b); | ||||
|   EXPECT_EQ(-0x7fffffffffffffffLL - 1,       c); | ||||
|   EXPECT_EQ(-1,                              d); | ||||
|   EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); | ||||
|   EXPECT_EQ(0x4e4249d27f8414a4LL,            f);   | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(8, true, &g)); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Unsigned1) { | ||||
|   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   cursor.set_big_endian(true); | ||||
|   int32_t a, b, c, d, e; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(1, false, &a) | ||||
|               .Read(1, false, &b) | ||||
|               .Read(1, false, &c) | ||||
|               .Read(1, false, &d)); | ||||
|   EXPECT_EQ(0,    a); | ||||
|   EXPECT_EQ(0x7f, b); | ||||
|   EXPECT_EQ(0x80, c); | ||||
|   EXPECT_EQ(0xff, d); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(1, false, &e)); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Unsigned2) { | ||||
|   uint8_t data[] = { 0x00, 0x00,   0x00, 0x80,   0x7f, 0xff, | ||||
|                      0x80, 0x00,   0x80, 0x80,   0xff, 0xff, | ||||
|                      0x39, 0xf1,   0x8a, 0xbc,   0x5a, 0xec }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer, true); | ||||
|   int64_t a, b, c, d, e, f, g, h, i, j; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(2, false, &a) | ||||
|               .Read(2, false, &b) | ||||
|               .Read(2, false, &c) | ||||
|               .Read(2, false, &d) | ||||
|               .Read(2, false, &e) | ||||
|               .Read(2, false, &f) | ||||
|               .Read(2, false, &g) | ||||
|               .Read(2, false, &h) | ||||
|               .Read(2, false, &i)); | ||||
|   EXPECT_EQ(0,      a); | ||||
|   EXPECT_EQ(0x80,   b); | ||||
|   EXPECT_EQ(0x7fff, c); | ||||
|   EXPECT_EQ(0x8000, d); | ||||
|   EXPECT_EQ(0x8080, e); | ||||
|   EXPECT_EQ(0xffff, f); | ||||
|   EXPECT_EQ(0x39f1, g); | ||||
|   EXPECT_EQ(0x8abc, h); | ||||
|   EXPECT_EQ(0x5aec, i); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(2, false, &j)); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Unsigned4) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||
|                      0x7f, 0xff, 0xff, 0xff, | ||||
|                      0x80, 0x00, 0x00, 0x00, | ||||
|                      0xff, 0xff, 0xff, 0xff, | ||||
|                      0xb6, 0xb1, 0xff, 0xef, | ||||
|                      0x19, 0x6a, 0xca, 0x46 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   cursor.set_big_endian(true); | ||||
|   int64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(4, false, &a) | ||||
|               .Read(4, false, &b) | ||||
|               .Read(4, false, &c) | ||||
|               .Read(4, false, &d) | ||||
|               .Read(4, false, &e) | ||||
|               .Read(4, false, &f)); | ||||
|   EXPECT_EQ(0,          a); | ||||
|   EXPECT_EQ(0x7fffffff, b); | ||||
|   EXPECT_EQ(0x80000000, c); | ||||
|   EXPECT_EQ(0xffffffff, d); | ||||
|   EXPECT_EQ(0xb6b1ffef, e); | ||||
|   EXPECT_EQ(0x196aca46, f); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(4, false, &g)); | ||||
| } | ||||
|  | ||||
| TEST(BigEndian, Unsigned8) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||
|                      0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||
|                      0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | ||||
|                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||
|                      0x93, 0x20, 0xd5, 0xe9, 0xd2, 0xd5, 0x87, 0x9c, | ||||
|                      0x4e, 0x42, 0x49, 0xd2, 0x7f, 0x84, 0x14, 0xa4 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer, true); | ||||
|   uint64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(8, false, &a) | ||||
|               .Read(8, false, &b) | ||||
|               .Read(8, false, &c) | ||||
|               .Read(8, false, &d) | ||||
|               .Read(8, false, &e) | ||||
|               .Read(8, false, &f)); | ||||
|   EXPECT_EQ(0U,                    a); | ||||
|   EXPECT_EQ(0x7fffffffffffffffULL, b); | ||||
|   EXPECT_EQ(0x8000000000000000ULL, c); | ||||
|   EXPECT_EQ(0xffffffffffffffffULL, d); | ||||
|   EXPECT_EQ(0x9320d5e9d2d5879cULL, e); | ||||
|   EXPECT_EQ(0x4e4249d27f8414a4ULL, f);   | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(8, false, &g)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Signed1) { | ||||
|   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int32_t a, b, c, d, e; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(1, true, &a) | ||||
|               .Read(1, true, &b) | ||||
|               .Read(1, true, &c) | ||||
|               .Read(1, true, &d)); | ||||
|   EXPECT_EQ(0,     a); | ||||
|   EXPECT_EQ(0x7f,  b); | ||||
|   EXPECT_EQ(-0x80, c); | ||||
|   EXPECT_EQ(-1,    d); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(1, true, &e)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Signed2) { | ||||
|   uint8_t data[] = { 0x00, 0x00,   0x80, 0x00,   0xff, 0x7f, | ||||
|                      0x00, 0x80,   0x80, 0x80,   0xff, 0xff, | ||||
|                      0xf1, 0x39,   0xbc, 0x8a,   0xec, 0x5a }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer, false); | ||||
|   int32_t a, b, c, d, e, f, g, h, i, j; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(2, true, &a) | ||||
|               .Read(2, true, &b) | ||||
|               .Read(2, true, &c) | ||||
|               .Read(2, true, &d) | ||||
|               .Read(2, true, &e) | ||||
|               .Read(2, true, &f) | ||||
|               .Read(2, true, &g) | ||||
|               .Read(2, true, &h) | ||||
|               .Read(2, true, &i)); | ||||
|   EXPECT_EQ(0,       a); | ||||
|   EXPECT_EQ(0x80,    b); | ||||
|   EXPECT_EQ(0x7fff,  c); | ||||
|   EXPECT_EQ(-0x8000, d); | ||||
|   EXPECT_EQ(-0x7f80, e); | ||||
|   EXPECT_EQ(-1,      f); | ||||
|   EXPECT_EQ(0x39f1,  g); | ||||
|   EXPECT_EQ(-0x7544, h); | ||||
|   EXPECT_EQ(0x5aec,  i); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(2, true, &j)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Signed4) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||
|                      0xff, 0xff, 0xff, 0x7f, | ||||
|                      0x00, 0x00, 0x00, 0x80, | ||||
|                      0xff, 0xff, 0xff, 0xff, | ||||
|                      0xef, 0xff, 0xb1, 0xb6,  | ||||
|                      0x46, 0xca, 0x6a, 0x19 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(4, true, &a) | ||||
|               .Read(4, true, &b) | ||||
|               .Read(4, true, &c) | ||||
|               .Read(4, true, &d) | ||||
|               .Read(4, true, &e) | ||||
|               .Read(4, true, &f)); | ||||
|   EXPECT_EQ(0,                    a); | ||||
|   EXPECT_EQ(0x7fffffff,           b); | ||||
|   EXPECT_EQ(-0x80000000LL,        c); | ||||
|   EXPECT_EQ(-1,                   d); | ||||
|   EXPECT_EQ((int32_t) 0xb6b1ffef, e); | ||||
|   EXPECT_EQ(0x196aca46,           f); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(4, true, &g)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Signed8) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||
|                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, | ||||
|                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, | ||||
|                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||
|                      0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, | ||||
|                      0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer, false); | ||||
|   int64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(8, true, &a) | ||||
|               .Read(8, true, &b) | ||||
|               .Read(8, true, &c) | ||||
|               .Read(8, true, &d) | ||||
|               .Read(8, true, &e) | ||||
|               .Read(8, true, &f)); | ||||
|   EXPECT_EQ(0,                               a); | ||||
|   EXPECT_EQ(0x7fffffffffffffffLL,            b); | ||||
|   EXPECT_EQ(-0x7fffffffffffffffLL - 1,       c); | ||||
|   EXPECT_EQ(-1,                              d); | ||||
|   EXPECT_EQ((int64_t) 0x9320d5e9d2d5879cULL, e); | ||||
|   EXPECT_EQ(0x4e4249d27f8414a4LL,            f);   | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(8, true, &g)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Unsigned1) { | ||||
|   uint8_t data[] = { 0x00, 0x7f, 0x80, 0xff }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int32_t a, b, c, d, e; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(1, false, &a) | ||||
|               .Read(1, false, &b) | ||||
|               .Read(1, false, &c) | ||||
|               .Read(1, false, &d)); | ||||
|   EXPECT_EQ(0,    a); | ||||
|   EXPECT_EQ(0x7f, b); | ||||
|   EXPECT_EQ(0x80, c); | ||||
|   EXPECT_EQ(0xff, d); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(1, false, &e)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Unsigned2) { | ||||
|   uint8_t data[] = { 0x00, 0x00,   0x80, 0x00,   0xff, 0x7f, | ||||
|                      0x00, 0x80,   0x80, 0x80,   0xff, 0xff, | ||||
|                      0xf1, 0x39,   0xbc, 0x8a,   0xec, 0x5a }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int32_t a, b, c, d, e, f, g, h, i, j; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(2, false, &a) | ||||
|               .Read(2, false, &b) | ||||
|               .Read(2, false, &c) | ||||
|               .Read(2, false, &d) | ||||
|               .Read(2, false, &e) | ||||
|               .Read(2, false, &f) | ||||
|               .Read(2, false, &g) | ||||
|               .Read(2, false, &h) | ||||
|               .Read(2, false, &i)); | ||||
|   EXPECT_EQ(0,      a); | ||||
|   EXPECT_EQ(0x80,   b); | ||||
|   EXPECT_EQ(0x7fff, c); | ||||
|   EXPECT_EQ(0x8000, d); | ||||
|   EXPECT_EQ(0x8080, e); | ||||
|   EXPECT_EQ(0xffff, f); | ||||
|   EXPECT_EQ(0x39f1, g); | ||||
|   EXPECT_EQ(0x8abc, h); | ||||
|   EXPECT_EQ(0x5aec, i); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(2, false, &j)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Unsigned4) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, | ||||
|                      0xff, 0xff, 0xff, 0x7f, | ||||
|                      0x00, 0x00, 0x00, 0x80, | ||||
|                      0xff, 0xff, 0xff, 0xff, | ||||
|                      0xef, 0xff, 0xb1, 0xb6, | ||||
|                      0x46, 0xca, 0x6a, 0x19 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(4, false, &a) | ||||
|               .Read(4, false, &b) | ||||
|               .Read(4, false, &c) | ||||
|               .Read(4, false, &d) | ||||
|               .Read(4, false, &e) | ||||
|               .Read(4, false, &f)); | ||||
|   EXPECT_EQ(0,          a); | ||||
|   EXPECT_EQ(0x7fffffff, b); | ||||
|   EXPECT_EQ(0x80000000, c); | ||||
|   EXPECT_EQ(0xffffffff, d); | ||||
|   EXPECT_EQ(0xb6b1ffef, e); | ||||
|   EXPECT_EQ(0x196aca46, f); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(4, false, &g)); | ||||
| } | ||||
|  | ||||
| TEST(LittleEndian, Unsigned8) { | ||||
|   uint8_t data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,  | ||||
|                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, | ||||
|                      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, | ||||
|                      0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||||
|                      0x9c, 0x87, 0xd5, 0xd2, 0xe9, 0xd5, 0x20, 0x93, | ||||
|                      0xa4, 0x14, 0x84, 0x7f, 0xd2, 0x49, 0x42, 0x4e }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   uint64_t a, b, c, d, e, f, g; | ||||
|   ASSERT_TRUE(cursor | ||||
|               .Read(8, false, &a) | ||||
|               .Read(8, false, &b) | ||||
|               .Read(8, false, &c) | ||||
|               .Read(8, false, &d) | ||||
|               .Read(8, false, &e) | ||||
|               .Read(8, false, &f)); | ||||
|   EXPECT_EQ(0U,                    a); | ||||
|   EXPECT_EQ(0x7fffffffffffffffULL, b); | ||||
|   EXPECT_EQ(0x8000000000000000ULL, c); | ||||
|   EXPECT_EQ(0xffffffffffffffffULL, d); | ||||
|   EXPECT_EQ(0x9320d5e9d2d5879cULL, e); | ||||
|   EXPECT_EQ(0x4e4249d27f8414a4ULL, f);   | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor.Read(8, false, &g)); | ||||
| } | ||||
|  | ||||
| TEST(Extractor, Signed1) { | ||||
|   uint8_t data[] = { 0xfd }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int8_t a; | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(-3, a); | ||||
|   EXPECT_FALSE(cursor >> a); | ||||
| } | ||||
|  | ||||
| TEST(Extractor, Signed2) { | ||||
|   uint8_t data[] = { 0x13, 0xcd }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int16_t a; | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(-13037, a); | ||||
|   EXPECT_FALSE(cursor >> a); | ||||
| } | ||||
|  | ||||
| TEST(Extractor, Signed4) { | ||||
|   uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   int32_t a; | ||||
|   // For some reason, G++ 4.4.1 complains: | ||||
|   //   warning: array subscript is above array bounds | ||||
|   // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but | ||||
|   // I'm not able to see how such a reference would occur. | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(-380377902, a); | ||||
|   EXPECT_FALSE(cursor >> a); | ||||
| } | ||||
|  | ||||
| TEST(Extractor, Unsigned1) { | ||||
|   uint8_t data[] = { 0xfd }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   uint8_t a; | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(0xfd, a); | ||||
|   EXPECT_FALSE(cursor >> a); | ||||
| } | ||||
|  | ||||
| TEST(Extractor, Unsigned2) { | ||||
|   uint8_t data[] = { 0x13, 0xcd }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   uint16_t a; | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(0xcd13, a); | ||||
|   EXPECT_FALSE(cursor >> a); | ||||
| } | ||||
|  | ||||
| TEST(Extractor, Unsigned4) { | ||||
|   uint8_t data[] = { 0xd2, 0xe4, 0x53, 0xe9 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   uint32_t a; | ||||
|   // For some reason, G++ 4.4.1 complains: | ||||
|   //   warning: array subscript is above array bounds | ||||
|   // in ByteCursor::Read(size_t, bool, T *) as it inlines this call, but | ||||
|   // I'm not able to see how such a reference would occur. | ||||
|   EXPECT_TRUE(cursor >> a); | ||||
|   EXPECT_EQ(0xe953e4d2, a); | ||||
|   EXPECT_FALSE(cursor >> a); | ||||
|   EXPECT_FALSE(cursor >> a); | ||||
| } | ||||
|  | ||||
| TEST(Extractor, Mixed) { | ||||
|   uint8_t data[] = { 0x42, | ||||
|                      0x25, 0x0b, | ||||
|                      0x3d, 0x25, 0xed, 0x2a, | ||||
|                      0xec, 0x16, 0x9e, 0x14, 0x61, 0x5b, 0x2c, 0xcf, | ||||
|                      0xd8, | ||||
|                      0x22, 0xa5, | ||||
|                      0x3a, 0x02, 0x6a, 0xd7, | ||||
|                      0x93, 0x2a, 0x2d, 0x8d, 0xb4, 0x95, 0xe0, 0xc6 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|   cursor.set_big_endian(true); | ||||
|  | ||||
|   uint8_t a; | ||||
|   uint16_t b; | ||||
|   uint32_t c; | ||||
|   uint64_t d; | ||||
|   int8_t e; | ||||
|   int16_t f; | ||||
|   int32_t g; | ||||
|   int64_t h; | ||||
|   int z; | ||||
|   EXPECT_FALSE(cursor.AtEnd()); | ||||
|   EXPECT_TRUE(cursor >> a >> b >> c >> d >> e >> f >> g >> h); | ||||
|   EXPECT_EQ(0x42U, a); | ||||
|   EXPECT_EQ(0x250bU, b); | ||||
|   EXPECT_EQ(0x3d25ed2aU, c); | ||||
|   EXPECT_EQ(0xec169e14615b2ccfULL, d); | ||||
|   EXPECT_EQ(-40, e); | ||||
|   EXPECT_EQ(0x22a5, f); | ||||
|   EXPECT_EQ(0x3a026ad7, g); | ||||
|   EXPECT_EQ(-7842405714468937530LL, h); | ||||
|  | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
|   EXPECT_FALSE(cursor >> z); | ||||
| } | ||||
|  | ||||
| TEST(Strings, Zero) { | ||||
|   uint8_t data[] = { 0xa6 }; | ||||
|   ByteBuffer buffer(data, 0); | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   uint8_t received[1]; | ||||
|   received[0] = 0xc2; | ||||
|   EXPECT_TRUE(cursor.Read(received, 0)); | ||||
|   EXPECT_EQ(0xc2U, received[0]); | ||||
| } | ||||
|  | ||||
| TEST(Strings, Some) { | ||||
|   uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   uint8_t received[7] = { 0xa7, 0xf7, 0x43, 0x0c, 0x27, 0xea, 0xed }; | ||||
|   EXPECT_TRUE(cursor.Skip(2).Read(received, 5)); | ||||
|   uint8_t expected[7] = { 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xea, 0xed }; | ||||
|   EXPECT_TRUE(memcmp(received, expected, 7) == 0); | ||||
| } | ||||
|  | ||||
| TEST(Strings, TooMuch) { | ||||
|   uint8_t data[] = { 0x5d, 0x31, 0x09, 0xa6, 0x2e, 0x2c, 0x83, 0xbb }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   uint8_t received1[3]; | ||||
|   uint8_t received2[3]; | ||||
|   uint8_t received3[3]; | ||||
|   EXPECT_FALSE(cursor | ||||
|                .Read(received1, 3) | ||||
|                .Read(received2, 3) | ||||
|                .Read(received3, 3)); | ||||
|   uint8_t expected1[3] = { 0x5d, 0x31, 0x09 }; | ||||
|   uint8_t expected2[3] = { 0xa6, 0x2e, 0x2c }; | ||||
|  | ||||
|   EXPECT_TRUE(memcmp(received1, expected1, 3) == 0); | ||||
|   EXPECT_TRUE(memcmp(received2, expected2, 3) == 0); | ||||
| } | ||||
|  | ||||
| TEST(Strings, PointTo) { | ||||
|   uint8_t data[] = { 0x83, 0x80, 0xb4, 0x38, 0x00, 0x2c, 0x0a, 0x27 }; | ||||
|   ByteBuffer buffer(data, sizeof(data)); | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   const uint8_t *received1; | ||||
|   const uint8_t *received2; | ||||
|   const uint8_t *received3; | ||||
|   const uint8_t *received4; | ||||
|   EXPECT_FALSE(cursor | ||||
|                .PointTo(&received1, 3) | ||||
|                .PointTo(&received2, 3) | ||||
|                .PointTo(&received3) | ||||
|                .PointTo(&received4, 3)); | ||||
|   EXPECT_EQ(data + 0, received1); | ||||
|   EXPECT_EQ(data + 3, received2); | ||||
|   EXPECT_EQ(data + 6, received3); | ||||
|   EXPECT_EQ(NULL, received4); | ||||
| } | ||||
|  | ||||
| TEST(Strings, CString) { | ||||
|   uint8_t data[] = "abc\0\0foo"; | ||||
|   ByteBuffer buffer(data, sizeof(data) - 1);  // don't include terminating '\0' | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   string a, b, c; | ||||
|   EXPECT_TRUE(cursor.CString(&a).CString(&b)); | ||||
|   EXPECT_EQ("abc", a); | ||||
|   EXPECT_EQ("", b); | ||||
|   EXPECT_FALSE(cursor.CString(&c)); | ||||
|   EXPECT_EQ("", c); | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
| } | ||||
|  | ||||
| TEST(Strings, CStringLimit) { | ||||
|   uint8_t data[] = "abcdef\0\0foobar"; | ||||
|   ByteBuffer buffer(data, sizeof(data) - 1);  // don't include terminating '\0' | ||||
|   ByteCursor cursor(&buffer); | ||||
|  | ||||
|   string a, b, c, d, e; | ||||
|  | ||||
|   EXPECT_TRUE(cursor.CString(&a, 3)); | ||||
|   EXPECT_EQ("abc", a); | ||||
|  | ||||
|   EXPECT_TRUE(cursor.CString(&b, 0)); | ||||
|   EXPECT_EQ("", b); | ||||
|  | ||||
|   EXPECT_TRUE(cursor.CString(&c, 6)); | ||||
|   EXPECT_EQ("def", c); | ||||
|  | ||||
|   EXPECT_TRUE(cursor.CString(&d, 4)); | ||||
|   EXPECT_EQ("ooba", d); | ||||
|  | ||||
|   EXPECT_FALSE(cursor.CString(&e, 4)); | ||||
|   EXPECT_EQ("", e); | ||||
|  | ||||
|   EXPECT_TRUE(cursor.AtEnd()); | ||||
| } | ||||
|  | ||||
| //  uint8_t data[] = { 0xa6, 0x54, 0xdf, 0x67, 0x51, 0x43, 0xac, 0xf1 }; | ||||
| //  ByteBuffer buffer(data, sizeof(data)); | ||||
							
								
								
									
										538
									
								
								sdk/breakpad/common/convert_UTF.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										538
									
								
								sdk/breakpad/common/convert_UTF.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,538 @@ | ||||
| /* | ||||
|  * Copyright 2001-2004 Unicode, Inc. | ||||
|  * | ||||
|  * Disclaimer | ||||
|  * | ||||
|  * This source code is provided as is by Unicode, Inc. No claims are | ||||
|  * made as to fitness for any particular purpose. No warranties of any | ||||
|  * kind are expressed or implied. The recipient agrees to determine | ||||
|  * applicability of information provided. If this file has been | ||||
|  * purchased on magnetic or optical media from Unicode, Inc., the | ||||
|  * sole remedy for any claim will be exchange of defective media | ||||
|  * within 90 days of receipt. | ||||
|  * | ||||
|  * Limitations on Rights to Redistribute This Code | ||||
|  * | ||||
|  * Unicode, Inc. hereby grants the right to freely use the information | ||||
|  * supplied in this file in the creation of products supporting the | ||||
|  * Unicode Standard, and to make copies of this file in any form | ||||
|  * for internal or external distribution as long as this notice | ||||
|  * remains attached. | ||||
|  */ | ||||
|  | ||||
| /* --------------------------------------------------------------------- | ||||
|  | ||||
| Conversions between UTF32, UTF-16, and UTF-8. Source code file. | ||||
| Author: Mark E. Davis, 1994. | ||||
| Rev History: Rick McGowan, fixes & updates May 2001. | ||||
| Sept 2001: fixed const & error conditions per | ||||
| mods suggested by S. Parent & A. Lillich. | ||||
| June 2002: Tim Dodd added detection and handling of incomplete | ||||
| source sequences, enhanced error detection, added casts | ||||
| to eliminate compiler warnings. | ||||
| July 2003: slight mods to back out aggressive FFFE detection. | ||||
| Jan 2004: updated switches in from-UTF8 conversions. | ||||
| Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions. | ||||
|  | ||||
| See the header file "ConvertUTF.h" for complete documentation. | ||||
|  | ||||
| ------------------------------------------------------------------------ */ | ||||
|  | ||||
|  | ||||
| #include "convert_UTF.h" | ||||
| #ifdef CVTUTF_DEBUG | ||||
| #include <stdio.h> | ||||
| #endif | ||||
|  | ||||
| static const int halfShift  = 10; /* used for shifting by 10 bits */ | ||||
|  | ||||
| static const UTF32 halfBase = 0x0010000UL; | ||||
| static const UTF32 halfMask = 0x3FFUL; | ||||
|  | ||||
| #define UNI_SUR_HIGH_START  (UTF32)0xD800 | ||||
| #define UNI_SUR_HIGH_END    (UTF32)0xDBFF | ||||
| #define UNI_SUR_LOW_START   (UTF32)0xDC00 | ||||
| #define UNI_SUR_LOW_END     (UTF32)0xDFFF | ||||
|  | ||||
| #ifndef false | ||||
| #define false	   0 | ||||
| #endif | ||||
| #ifndef true | ||||
| #define true	    1 | ||||
| #endif | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||
|                                       UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { | ||||
|   ConversionResult result = conversionOK; | ||||
|   const UTF32* source = *sourceStart; | ||||
|   UTF16* target = *targetStart; | ||||
|   while (source < sourceEnd) { | ||||
|     UTF32 ch; | ||||
|     if (target >= targetEnd) { | ||||
| 	    result = targetExhausted; break; | ||||
|     } | ||||
|     ch = *source++; | ||||
|     if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ | ||||
| 	    /* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */ | ||||
| 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||
|         if (flags == strictConversion) { | ||||
|           --source; /* return to the illegal value itself */ | ||||
|           result = sourceIllegal; | ||||
|           break; | ||||
|         } else { | ||||
|           *target++ = UNI_REPLACEMENT_CHAR; | ||||
|         } | ||||
| 	    } else { | ||||
|         *target++ = (UTF16)ch; /* normal case */ | ||||
| 	    } | ||||
|     } else if (ch > UNI_MAX_LEGAL_UTF32) { | ||||
| 	    if (flags == strictConversion) { | ||||
|         result = sourceIllegal; | ||||
| 	    } else { | ||||
|         *target++ = UNI_REPLACEMENT_CHAR; | ||||
| 	    } | ||||
|     } else { | ||||
| 	    /* target is a character in range 0xFFFF - 0x10FFFF. */ | ||||
| 	    if (target + 1 >= targetEnd) { | ||||
|         --source; /* Back up source pointer! */ | ||||
|         result = targetExhausted; break; | ||||
| 	    } | ||||
| 	    ch -= halfBase; | ||||
| 	    *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); | ||||
| 	    *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); | ||||
|     } | ||||
|   } | ||||
| *sourceStart = source; | ||||
| *targetStart = target; | ||||
| return result; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||
|                                       UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { | ||||
|   ConversionResult result = conversionOK; | ||||
|   const UTF16* source = *sourceStart; | ||||
|   UTF32* target = *targetStart; | ||||
|   UTF32 ch, ch2; | ||||
|   while (source < sourceEnd) { | ||||
|     const UTF16* oldSource = source; /*  In case we have to back up because of target overflow. */ | ||||
|     ch = *source++; | ||||
|     /* If we have a surrogate pair, convert to UTF32 first. */ | ||||
|     if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { | ||||
| 	    /* If the 16 bits following the high surrogate are in the source buffer... */ | ||||
| 	    if (source < sourceEnd) { | ||||
|         ch2 = *source; | ||||
|         /* If it's a low surrogate, convert to UTF32. */ | ||||
|         if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { | ||||
|           ch = ((ch - UNI_SUR_HIGH_START) << halfShift) | ||||
|           + (ch2 - UNI_SUR_LOW_START) + halfBase; | ||||
|           ++source; | ||||
|         } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ | ||||
|           --source; /* return to the illegal value itself */ | ||||
|           result = sourceIllegal; | ||||
|           break; | ||||
|         } | ||||
| 	    } else { /* We don't have the 16 bits following the high surrogate. */ | ||||
|         --source; /* return to the high surrogate */ | ||||
|         result = sourceExhausted; | ||||
|         break; | ||||
| 	    } | ||||
|     } else if (flags == strictConversion) { | ||||
| 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||
| 	    if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { | ||||
|         --source; /* return to the illegal value itself */ | ||||
|         result = sourceIllegal; | ||||
|         break; | ||||
| 	    } | ||||
|     } | ||||
|     if (target >= targetEnd) { | ||||
| 	    source = oldSource; /* Back up source pointer! */ | ||||
| 	    result = targetExhausted; break; | ||||
|     } | ||||
|     *target++ = ch; | ||||
|   } | ||||
|   *sourceStart = source; | ||||
|   *targetStart = target; | ||||
| #ifdef CVTUTF_DEBUG | ||||
|   if (result == sourceIllegal) { | ||||
|     fprintf(stderr, "ConvertUTF16toUTF32 illegal seq 0x%04x,%04x\n", ch, ch2); | ||||
|     fflush(stderr); | ||||
|   } | ||||
| #endif | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| /* | ||||
|  * Index into the table below with the first byte of a UTF-8 sequence to | ||||
|  * get the number of trailing bytes that are supposed to follow it. | ||||
|  * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is | ||||
|  * left as-is for anyone who may want to do such conversion, which was | ||||
|  * allowed in earlier algorithms. | ||||
|  */ | ||||
| static const char trailingBytesForUTF8[256] = { | ||||
|   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||
|   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||
|   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||
|   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||
|   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||
|   0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, | ||||
|   1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, | ||||
|   2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Magic values subtracted from a buffer value during UTF8 conversion. | ||||
|  * This table contains as many values as there might be trailing bytes | ||||
|  * in a UTF-8 sequence. | ||||
|  */ | ||||
| static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, | ||||
|   0x03C82080UL, 0xFA082080UL, 0x82082080UL }; | ||||
|  | ||||
| /* | ||||
|  * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed | ||||
|  * into the first byte, depending on how many bytes follow.  There are | ||||
|  * as many entries in this table as there are UTF-8 sequence types. | ||||
|  * (I.e., one byte sequence, two byte... etc.). Remember that sequencs | ||||
|  * for *legal* UTF-8 will be 4 or fewer bytes total. | ||||
|  */ | ||||
| static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| /* The interface converts a whole buffer to avoid function-call overhead. | ||||
| * Constants have been gathered. Loops & conditionals have been removed as | ||||
| * much as possible for efficiency, in favor of drop-through switches. | ||||
| * (See "Note A" at the bottom of the file for equivalent code.) | ||||
| * If your compiler supports it, the "isLegalUTF8" call can be turned | ||||
| * into an inline function. | ||||
| */ | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||
|                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { | ||||
|   ConversionResult result = conversionOK; | ||||
|   const UTF16* source = *sourceStart; | ||||
|   UTF8* target = *targetStart; | ||||
|   while (source < sourceEnd) { | ||||
|     UTF32 ch; | ||||
|     unsigned short bytesToWrite = 0; | ||||
|     const UTF32 byteMask = 0xBF; | ||||
|     const UTF32 byteMark = 0x80; | ||||
|     const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ | ||||
|     ch = *source++; | ||||
|     /* If we have a surrogate pair, convert to UTF32 first. */ | ||||
|     if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END) { | ||||
| 	    /* If the 16 bits following the high surrogate are in the source buffer... */ | ||||
| 	    if (source < sourceEnd) { | ||||
|         UTF32 ch2 = *source; | ||||
|         /* If it's a low surrogate, convert to UTF32. */ | ||||
|         if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { | ||||
|           ch = ((ch - UNI_SUR_HIGH_START) << halfShift) | ||||
|           + (ch2 - UNI_SUR_LOW_START) + halfBase; | ||||
|           ++source; | ||||
|         } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ | ||||
|           --source; /* return to the illegal value itself */ | ||||
|           result = sourceIllegal; | ||||
|           break; | ||||
|         } | ||||
| 	    } else { /* We don't have the 16 bits following the high surrogate. */ | ||||
|         --source; /* return to the high surrogate */ | ||||
|         result = sourceExhausted; | ||||
|         break; | ||||
| 	    } | ||||
|     } else if (flags == strictConversion) { | ||||
| 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||
| 	    if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END) { | ||||
|         --source; /* return to the illegal value itself */ | ||||
|         result = sourceIllegal; | ||||
|         break; | ||||
| 	    } | ||||
|     } | ||||
|     /* Figure out how many bytes the result will require */ | ||||
|     if (ch < (UTF32)0x80) {	     bytesToWrite = 1; | ||||
|     } else if (ch < (UTF32)0x800) {     bytesToWrite = 2; | ||||
|     } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3; | ||||
|     } else if (ch < (UTF32)0x110000) {  bytesToWrite = 4; | ||||
|     } else {			    bytesToWrite = 3; | ||||
|       ch = UNI_REPLACEMENT_CHAR; | ||||
|     } | ||||
|  | ||||
|     target += bytesToWrite; | ||||
|     if (target > targetEnd) { | ||||
| 	    source = oldSource; /* Back up source pointer! */ | ||||
| 	    target -= bytesToWrite; result = targetExhausted; break; | ||||
|     } | ||||
|     switch (bytesToWrite) { /* note: everything falls through. */ | ||||
| 	    case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||
| 	    case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||
| 	    case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||
| 	    case 1: *--target =  (UTF8)(ch | firstByteMark[bytesToWrite]); | ||||
|     } | ||||
|     target += bytesToWrite; | ||||
|   } | ||||
| *sourceStart = source; | ||||
| *targetStart = target; | ||||
| return result; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| /* | ||||
|  * Utility routine to tell whether a sequence of bytes is legal UTF-8. | ||||
|  * This must be called with the length pre-determined by the first byte. | ||||
|  * If not calling this from ConvertUTF8to*, then the length can be set by: | ||||
|  *  length = trailingBytesForUTF8[*source]+1; | ||||
|  * and the sequence is illegal right away if there aren't that many bytes | ||||
|  * available. | ||||
|  * If presented with a length > 4, this returns false.  The Unicode | ||||
|  * definition of UTF-8 goes up to 4-byte sequences. | ||||
|  */ | ||||
|  | ||||
| static Boolean isLegalUTF8(const UTF8 *source, int length) { | ||||
|   UTF8 a; | ||||
|   const UTF8 *srcptr = source+length; | ||||
|   switch (length) { | ||||
|     default: return false; | ||||
|       /* Everything else falls through when "true"... */ | ||||
|     case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; | ||||
|     case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; | ||||
|     case 2: if ((a = (*--srcptr)) > 0xBF) return false; | ||||
|  | ||||
|       switch (*source) { | ||||
|         /* no fall-through in this inner switch */ | ||||
|         case 0xE0: if (a < 0xA0) return false; break; | ||||
|         case 0xED: if (a > 0x9F) return false; break; | ||||
|         case 0xF0: if (a < 0x90) return false; break; | ||||
|         case 0xF4: if (a > 0x8F) return false; break; | ||||
|         default:   if (a < 0x80) return false; | ||||
|       } | ||||
|  | ||||
|       case 1: if (*source >= 0x80 && *source < 0xC2) return false; | ||||
|   } | ||||
|   if (*source > 0xF4) return false; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| /* | ||||
|  * Exported function to return whether a UTF-8 sequence is legal or not. | ||||
|  * This is not used here; it's just exported. | ||||
|  */ | ||||
| Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { | ||||
|   int length = trailingBytesForUTF8[*source]+1; | ||||
|   if (source+length > sourceEnd) { | ||||
|     return false; | ||||
|   } | ||||
|   return isLegalUTF8(source, length); | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||
|                                      UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { | ||||
|   ConversionResult result = conversionOK; | ||||
|   const UTF8* source = *sourceStart; | ||||
|   UTF16* target = *targetStart; | ||||
|   while (source < sourceEnd) { | ||||
|     UTF32 ch = 0; | ||||
|     unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; | ||||
|     if (source + extraBytesToRead >= sourceEnd) { | ||||
| 	    result = sourceExhausted; break; | ||||
|     } | ||||
|     /* Do this check whether lenient or strict */ | ||||
|     if (! isLegalUTF8(source, extraBytesToRead+1)) { | ||||
| 	    result = sourceIllegal; | ||||
| 	    break; | ||||
|     } | ||||
|     /* | ||||
|      * The cases all fall through. See "Note A" below. | ||||
|      */ | ||||
|     switch (extraBytesToRead) { | ||||
| 	    case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ | ||||
| 	    case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */ | ||||
| 	    case 3: ch += *source++; ch <<= 6; | ||||
| 	    case 2: ch += *source++; ch <<= 6; | ||||
| 	    case 1: ch += *source++; ch <<= 6; | ||||
| 	    case 0: ch += *source++; | ||||
|     } | ||||
|     ch -= offsetsFromUTF8[extraBytesToRead]; | ||||
|  | ||||
|     if (target >= targetEnd) { | ||||
| 	    source -= (extraBytesToRead+1); /* Back up source pointer! */ | ||||
| 	    result = targetExhausted; break; | ||||
|     } | ||||
|     if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ | ||||
| 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||
| 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||
|         if (flags == strictConversion) { | ||||
|           source -= (extraBytesToRead+1); /* return to the illegal value itself */ | ||||
|           result = sourceIllegal; | ||||
|           break; | ||||
|         } else { | ||||
|           *target++ = UNI_REPLACEMENT_CHAR; | ||||
|         } | ||||
| 	    } else { | ||||
|         *target++ = (UTF16)ch; /* normal case */ | ||||
| 	    } | ||||
|     } else if (ch > UNI_MAX_UTF16) { | ||||
| 	    if (flags == strictConversion) { | ||||
|         result = sourceIllegal; | ||||
|         source -= (extraBytesToRead+1); /* return to the start */ | ||||
|         break; /* Bail out; shouldn't continue */ | ||||
| 	    } else { | ||||
|         *target++ = UNI_REPLACEMENT_CHAR; | ||||
| 	    } | ||||
|     } else { | ||||
| 	    /* target is a character in range 0xFFFF - 0x10FFFF. */ | ||||
| 	    if (target + 1 >= targetEnd) { | ||||
|         source -= (extraBytesToRead+1); /* Back up source pointer! */ | ||||
|         result = targetExhausted; break; | ||||
| 	    } | ||||
| 	    ch -= halfBase; | ||||
| 	    *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); | ||||
| 	    *target++ = (UTF16)((ch & halfMask) + UNI_SUR_LOW_START); | ||||
|     } | ||||
|   } | ||||
| *sourceStart = source; | ||||
| *targetStart = target; | ||||
| return result; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||
|                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { | ||||
|   ConversionResult result = conversionOK; | ||||
|   const UTF32* source = *sourceStart; | ||||
|   UTF8* target = *targetStart; | ||||
|   while (source < sourceEnd) { | ||||
|     UTF32 ch; | ||||
|     unsigned short bytesToWrite = 0; | ||||
|     const UTF32 byteMask = 0xBF; | ||||
|     const UTF32 byteMark = 0x80; | ||||
|     ch = *source++; | ||||
|     if (flags == strictConversion ) { | ||||
| 	    /* UTF-16 surrogate values are illegal in UTF-32 */ | ||||
| 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||
|         --source; /* return to the illegal value itself */ | ||||
|         result = sourceIllegal; | ||||
|         break; | ||||
| 	    } | ||||
|     } | ||||
|     /* | ||||
|      * Figure out how many bytes the result will require. Turn any | ||||
|      * illegally large UTF32 things (> Plane 17) into replacement chars. | ||||
|      */ | ||||
|     if (ch < (UTF32)0x80) {	     bytesToWrite = 1; | ||||
|     } else if (ch < (UTF32)0x800) {     bytesToWrite = 2; | ||||
|     } else if (ch < (UTF32)0x10000) {   bytesToWrite = 3; | ||||
|     } else if (ch <= UNI_MAX_LEGAL_UTF32) {  bytesToWrite = 4; | ||||
|     } else {			    bytesToWrite = 3; | ||||
|       ch = UNI_REPLACEMENT_CHAR; | ||||
|       result = sourceIllegal; | ||||
|     } | ||||
|  | ||||
|     target += bytesToWrite; | ||||
|     if (target > targetEnd) { | ||||
| 	    --source; /* Back up source pointer! */ | ||||
| 	    target -= bytesToWrite; result = targetExhausted; break; | ||||
|     } | ||||
|     switch (bytesToWrite) { /* note: everything falls through. */ | ||||
| 	    case 4: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||
| 	    case 3: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||
| 	    case 2: *--target = (UTF8)((ch | byteMark) & byteMask); ch >>= 6; | ||||
| 	    case 1: *--target = (UTF8) (ch | firstByteMark[bytesToWrite]); | ||||
|     } | ||||
|     target += bytesToWrite; | ||||
|   } | ||||
| *sourceStart = source; | ||||
| *targetStart = target; | ||||
| return result; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||
|                                      UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags) { | ||||
|   ConversionResult result = conversionOK; | ||||
|   const UTF8* source = *sourceStart; | ||||
|   UTF32* target = *targetStart; | ||||
|   while (source < sourceEnd) { | ||||
|     UTF32 ch = 0; | ||||
|     unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; | ||||
|     if (source + extraBytesToRead >= sourceEnd) { | ||||
| 	    result = sourceExhausted; break; | ||||
|     } | ||||
|     /* Do this check whether lenient or strict */ | ||||
|     if (! isLegalUTF8(source, extraBytesToRead+1)) { | ||||
| 	    result = sourceIllegal; | ||||
| 	    break; | ||||
|     } | ||||
|     /* | ||||
|      * The cases all fall through. See "Note A" below. | ||||
|      */ | ||||
|     switch (extraBytesToRead) { | ||||
| 	    case 5: ch += *source++; ch <<= 6; | ||||
| 	    case 4: ch += *source++; ch <<= 6; | ||||
| 	    case 3: ch += *source++; ch <<= 6; | ||||
| 	    case 2: ch += *source++; ch <<= 6; | ||||
| 	    case 1: ch += *source++; ch <<= 6; | ||||
| 	    case 0: ch += *source++; | ||||
|     } | ||||
|     ch -= offsetsFromUTF8[extraBytesToRead]; | ||||
|  | ||||
|     if (target >= targetEnd) { | ||||
| 	    source -= (extraBytesToRead+1); /* Back up the source pointer! */ | ||||
| 	    result = targetExhausted; break; | ||||
|     } | ||||
|     if (ch <= UNI_MAX_LEGAL_UTF32) { | ||||
| 	    /* | ||||
| 	     * UTF-16 surrogate values are illegal in UTF-32, and anything | ||||
| 	     * over Plane 17 (> 0x10FFFF) is illegal. | ||||
| 	     */ | ||||
| 	    if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END) { | ||||
|         if (flags == strictConversion) { | ||||
|           source -= (extraBytesToRead+1); /* return to the illegal value itself */ | ||||
|           result = sourceIllegal; | ||||
|           break; | ||||
|         } else { | ||||
|           *target++ = UNI_REPLACEMENT_CHAR; | ||||
|         } | ||||
| 	    } else { | ||||
|         *target++ = ch; | ||||
| 	    } | ||||
|     } else { /* i.e., ch > UNI_MAX_LEGAL_UTF32 */ | ||||
| 	    result = sourceIllegal; | ||||
| 	    *target++ = UNI_REPLACEMENT_CHAR; | ||||
|     } | ||||
|   } | ||||
|   *sourceStart = source; | ||||
|   *targetStart = target; | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| /* --------------------------------------------------------------------- | ||||
|  | ||||
| Note A. | ||||
| The fall-through switches in UTF-8 reading code save a | ||||
| temp variable, some decrements & conditionals.  The switches | ||||
| are equivalent to the following loop: | ||||
| { | ||||
|   int tmpBytesToRead = extraBytesToRead+1; | ||||
|   do { | ||||
| 		ch += *source++; | ||||
| 		--tmpBytesToRead; | ||||
| 		if (tmpBytesToRead) ch <<= 6; | ||||
|   } while (tmpBytesToRead > 0); | ||||
| } | ||||
| In UTF-8 writing code, the switches on "bytesToWrite" are | ||||
| similarly unrolled loops. | ||||
|  | ||||
| --------------------------------------------------------------------- */ | ||||
							
								
								
									
										148
									
								
								sdk/breakpad/common/convert_UTF.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								sdk/breakpad/common/convert_UTF.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| /* | ||||
|  * Copyright 2001-2004 Unicode, Inc. | ||||
|  * | ||||
|  * Disclaimer | ||||
|  * | ||||
|  * This source code is provided as is by Unicode, Inc. No claims are | ||||
|  * made as to fitness for any particular purpose. No warranties of any | ||||
|  * kind are expressed or implied. The recipient agrees to determine | ||||
|  * applicability of information provided. If this file has been | ||||
|  * purchased on magnetic or optical media from Unicode, Inc., the | ||||
|  * sole remedy for any claim will be exchange of defective media | ||||
|  * within 90 days of receipt. | ||||
|  * | ||||
|  * Limitations on Rights to Redistribute This Code | ||||
|  * | ||||
|  * Unicode, Inc. hereby grants the right to freely use the information | ||||
|  * supplied in this file in the creation of products supporting the | ||||
|  * Unicode Standard, and to make copies of this file in any form | ||||
|  * for internal or external distribution as long as this notice | ||||
|  * remains attached. | ||||
|  */ | ||||
|  | ||||
| #ifndef COMMON_CONVERT_UTF_H_ | ||||
| #define COMMON_CONVERT_UTF_H_ | ||||
|  | ||||
| /* --------------------------------------------------------------------- | ||||
|  | ||||
| Conversions between UTF32, UTF-16, and UTF-8.  Header file. | ||||
|  | ||||
| Several funtions are included here, forming a complete set of | ||||
| conversions between the three formats.  UTF-7 is not included | ||||
| here, but is handled in a separate source file. | ||||
|  | ||||
| Each of these routines takes pointers to input buffers and output | ||||
| buffers.  The input buffers are const. | ||||
|  | ||||
| Each routine converts the text between *sourceStart and sourceEnd, | ||||
| putting the result into the buffer between *targetStart and | ||||
| targetEnd. Note: the end pointers are *after* the last item: e.g. | ||||
| *(sourceEnd - 1) is the last item. | ||||
|  | ||||
| The return result indicates whether the conversion was successful, | ||||
| and if not, whether the problem was in the source or target buffers. | ||||
| (Only the first encountered problem is indicated.) | ||||
|  | ||||
| After the conversion, *sourceStart and *targetStart are both | ||||
| updated to point to the end of last text successfully converted in | ||||
| the respective buffers. | ||||
|  | ||||
| Input parameters: | ||||
| sourceStart - pointer to a pointer to the source buffer. | ||||
| The contents of this are modified on return so that | ||||
| it points at the next thing to be converted. | ||||
| targetStart - similarly, pointer to pointer to the target buffer. | ||||
| sourceEnd, targetEnd - respectively pointers to the ends of the | ||||
| two buffers, for overflow checking only. | ||||
|  | ||||
| These conversion functions take a ConversionFlags argument. When this | ||||
| flag is set to strict, both irregular sequences and isolated surrogates | ||||
| will cause an error.  When the flag is set to lenient, both irregular | ||||
| sequences and isolated surrogates are converted. | ||||
|  | ||||
| Whether the flag is strict or lenient, all illegal sequences will cause | ||||
| an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>, | ||||
| or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code | ||||
| must check for illegal sequences. | ||||
|  | ||||
| When the flag is set to lenient, characters over 0x10FFFF are converted | ||||
| to the replacement character; otherwise (when the flag is set to strict) | ||||
| they constitute an error. | ||||
|  | ||||
| Output parameters: | ||||
| The value "sourceIllegal" is returned from some routines if the input | ||||
| sequence is malformed.  When "sourceIllegal" is returned, the source | ||||
| value will point to the illegal value that caused the problem. E.g., | ||||
| in UTF-8 when a sequence is malformed, it points to the start of the | ||||
| malformed sequence. | ||||
|  | ||||
| Author: Mark E. Davis, 1994. | ||||
| Rev History: Rick McGowan, fixes & updates May 2001. | ||||
| Fixes & updates, Sept 2001. | ||||
|  | ||||
| ------------------------------------------------------------------------ */ | ||||
|  | ||||
| /* --------------------------------------------------------------------- | ||||
| The following 4 definitions are compiler-specific. | ||||
| The C standard does not guarantee that wchar_t has at least | ||||
| 16 bits, so wchar_t is no less portable than unsigned short! | ||||
| All should be unsigned values to avoid sign extension during | ||||
| bit mask & shift operations. | ||||
| ------------------------------------------------------------------------ */ | ||||
|  | ||||
| typedef unsigned long	UTF32;	/* at least 32 bits */ | ||||
| typedef unsigned short	UTF16;	/* at least 16 bits */ | ||||
| typedef unsigned char	UTF8;	/* typically 8 bits */ | ||||
| typedef unsigned char	Boolean; /* 0 or 1 */ | ||||
|  | ||||
| /* Some fundamental constants */ | ||||
| #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD | ||||
| #define UNI_MAX_BMP (UTF32)0x0000FFFF | ||||
| #define UNI_MAX_UTF16 (UTF32)0x0010FFFF | ||||
| #define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF | ||||
| #define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF | ||||
|  | ||||
| typedef enum { | ||||
| 	conversionOK, 		/* conversion successful */ | ||||
| 	sourceExhausted,	/* partial character in source, but hit end */ | ||||
| 	targetExhausted,	/* insuff. room in target for conversion */ | ||||
| 	sourceIllegal		/* source sequence is illegal/malformed */ | ||||
| } ConversionResult; | ||||
|  | ||||
| typedef enum { | ||||
| 	strictConversion = 0, | ||||
| 	lenientConversion | ||||
| } ConversionFlags; | ||||
|  | ||||
| /* This is for C++ and does no harm in C */ | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| ConversionResult ConvertUTF8toUTF16 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||
|                                      UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); | ||||
|  | ||||
| ConversionResult ConvertUTF16toUTF8 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||
|                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); | ||||
|  | ||||
| ConversionResult ConvertUTF8toUTF32 (const UTF8** sourceStart, const UTF8* sourceEnd, | ||||
|                                      UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); | ||||
|  | ||||
| ConversionResult ConvertUTF32toUTF8 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||
|                                      UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); | ||||
|  | ||||
| ConversionResult ConvertUTF16toUTF32 (const UTF16** sourceStart, const UTF16* sourceEnd, | ||||
|                                       UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); | ||||
|  | ||||
| ConversionResult ConvertUTF32toUTF16 (const UTF32** sourceStart, const UTF32* sourceEnd, | ||||
|                                       UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); | ||||
|  | ||||
| Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /* --------------------------------------------------------------------- */ | ||||
|  | ||||
| #endif  // COMMON_CONVERT_UTF_H_ | ||||
							
								
								
									
										175
									
								
								sdk/breakpad/common/dwarf/bytereader-inl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								sdk/breakpad/common/dwarf/bytereader-inl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,175 @@ | ||||
| // Copyright 2006 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__ | ||||
| #define UTIL_DEBUGINFO_BYTEREADER_INL_H__ | ||||
|  | ||||
| #include "common/dwarf/bytereader.h" | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| inline uint8 ByteReader::ReadOneByte(const char* buffer) const { | ||||
|   return buffer[0]; | ||||
| } | ||||
|  | ||||
| inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const { | ||||
|   const unsigned char *buffer | ||||
|     = reinterpret_cast<const unsigned char *>(signed_buffer); | ||||
|   const uint16 buffer0 = buffer[0]; | ||||
|   const uint16 buffer1 = buffer[1]; | ||||
|   if (endian_ == ENDIANNESS_LITTLE) { | ||||
|     return buffer0 | buffer1 << 8; | ||||
|   } else { | ||||
|     return buffer1 | buffer0 << 8; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const { | ||||
|   const unsigned char *buffer | ||||
|     = reinterpret_cast<const unsigned char *>(signed_buffer); | ||||
|   const uint32 buffer0 = buffer[0]; | ||||
|   const uint32 buffer1 = buffer[1]; | ||||
|   const uint32 buffer2 = buffer[2]; | ||||
|   const uint32 buffer3 = buffer[3]; | ||||
|   if (endian_ == ENDIANNESS_LITTLE) { | ||||
|     return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; | ||||
|   } else { | ||||
|     return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24; | ||||
|   } | ||||
| } | ||||
|  | ||||
| inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const { | ||||
|   const unsigned char *buffer | ||||
|     = reinterpret_cast<const unsigned char *>(signed_buffer); | ||||
|   const uint64 buffer0 = buffer[0]; | ||||
|   const uint64 buffer1 = buffer[1]; | ||||
|   const uint64 buffer2 = buffer[2]; | ||||
|   const uint64 buffer3 = buffer[3]; | ||||
|   const uint64 buffer4 = buffer[4]; | ||||
|   const uint64 buffer5 = buffer[5]; | ||||
|   const uint64 buffer6 = buffer[6]; | ||||
|   const uint64 buffer7 = buffer[7]; | ||||
|   if (endian_ == ENDIANNESS_LITTLE) { | ||||
|     return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | | ||||
|       buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; | ||||
|   } else { | ||||
|     return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 | | ||||
|       buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Read an unsigned LEB128 number.  Each byte contains 7 bits of | ||||
| // information, plus one bit saying whether the number continues or | ||||
| // not. | ||||
|  | ||||
| inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer, | ||||
|                                              size_t* len) const { | ||||
|   uint64 result = 0; | ||||
|   size_t num_read = 0; | ||||
|   unsigned int shift = 0; | ||||
|   unsigned char byte; | ||||
|  | ||||
|   do { | ||||
|     byte = *buffer++; | ||||
|     num_read++; | ||||
|  | ||||
|     result |= (static_cast<uint64>(byte & 0x7f)) << shift; | ||||
|  | ||||
|     shift += 7; | ||||
|  | ||||
|   } while (byte & 0x80); | ||||
|  | ||||
|   *len = num_read; | ||||
|  | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| // Read a signed LEB128 number.  These are like regular LEB128 | ||||
| // numbers, except the last byte may have a sign bit set. | ||||
|  | ||||
| inline int64 ByteReader::ReadSignedLEB128(const char* buffer, | ||||
|                                           size_t* len) const { | ||||
|   int64 result = 0; | ||||
|   unsigned int shift = 0; | ||||
|   size_t num_read = 0; | ||||
|   unsigned char byte; | ||||
|  | ||||
|   do { | ||||
|       byte = *buffer++; | ||||
|       num_read++; | ||||
|       result |= (static_cast<uint64>(byte & 0x7f) << shift); | ||||
|       shift += 7; | ||||
|   } while (byte & 0x80); | ||||
|  | ||||
|   if ((shift < 8 * sizeof (result)) && (byte & 0x40)) | ||||
|     result |= -((static_cast<int64>(1)) << shift); | ||||
|   *len = num_read; | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| inline uint64 ByteReader::ReadOffset(const char* buffer) const { | ||||
|   assert(this->offset_reader_); | ||||
|   return (this->*offset_reader_)(buffer); | ||||
| } | ||||
|  | ||||
| inline uint64 ByteReader::ReadAddress(const char* buffer) const { | ||||
|   assert(this->address_reader_); | ||||
|   return (this->*address_reader_)(buffer); | ||||
| } | ||||
|  | ||||
| inline void ByteReader::SetCFIDataBase(uint64 section_base, | ||||
|                                        const char *buffer_base) { | ||||
|   section_base_ = section_base; | ||||
|   buffer_base_ = buffer_base; | ||||
|   have_section_base_ = true; | ||||
| } | ||||
|  | ||||
| inline void ByteReader::SetTextBase(uint64 text_base) { | ||||
|   text_base_ = text_base; | ||||
|   have_text_base_ = true; | ||||
| } | ||||
|  | ||||
| inline void ByteReader::SetDataBase(uint64 data_base) { | ||||
|   data_base_ = data_base; | ||||
|   have_data_base_ = true; | ||||
| } | ||||
|  | ||||
| inline void ByteReader::SetFunctionBase(uint64 function_base) { | ||||
|   function_base_ = function_base; | ||||
|   have_function_base_ = true; | ||||
| } | ||||
|  | ||||
| inline void ByteReader::ClearFunctionBase() { | ||||
|   have_function_base_ = false; | ||||
| } | ||||
|  | ||||
| }  // namespace dwarf2reader | ||||
|  | ||||
| #endif  // UTIL_DEBUGINFO_BYTEREADER_INL_H__ | ||||
							
								
								
									
										245
									
								
								sdk/breakpad/common/dwarf/bytereader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										245
									
								
								sdk/breakpad/common/dwarf/bytereader.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,245 @@ | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include "common/dwarf/bytereader-inl.h" | ||||
| #include "common/dwarf/bytereader.h" | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| ByteReader::ByteReader(enum Endianness endian) | ||||
|     :offset_reader_(NULL), address_reader_(NULL), endian_(endian), | ||||
|      address_size_(0), offset_size_(0), | ||||
|      have_section_base_(), have_text_base_(), have_data_base_(), | ||||
|      have_function_base_() { } | ||||
|  | ||||
| ByteReader::~ByteReader() { } | ||||
|  | ||||
| void ByteReader::SetOffsetSize(uint8 size) { | ||||
|   offset_size_ = size; | ||||
|   assert(size == 4 || size == 8); | ||||
|   if (size == 4) { | ||||
|     this->offset_reader_ = &ByteReader::ReadFourBytes; | ||||
|   } else { | ||||
|     this->offset_reader_ = &ByteReader::ReadEightBytes; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void ByteReader::SetAddressSize(uint8 size) { | ||||
|   address_size_ = size; | ||||
|   assert(size == 4 || size == 8); | ||||
|   if (size == 4) { | ||||
|     this->address_reader_ = &ByteReader::ReadFourBytes; | ||||
|   } else { | ||||
|     this->address_reader_ = &ByteReader::ReadEightBytes; | ||||
|   } | ||||
| } | ||||
|  | ||||
| uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) { | ||||
|   const uint64 initial_length = ReadFourBytes(start); | ||||
|   start += 4; | ||||
|  | ||||
|   // In DWARF2/3, if the initial length is all 1 bits, then the offset | ||||
|   // size is 8 and we need to read the next 8 bytes for the real length. | ||||
|   if (initial_length == 0xffffffff) { | ||||
|     SetOffsetSize(8); | ||||
|     *len = 12; | ||||
|     return ReadOffset(start); | ||||
|   } else { | ||||
|     SetOffsetSize(4); | ||||
|     *len = 4; | ||||
|   } | ||||
|   return initial_length; | ||||
| } | ||||
|  | ||||
| bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const { | ||||
|   if (encoding == DW_EH_PE_omit) return true; | ||||
|   if (encoding == DW_EH_PE_aligned) return true; | ||||
|   if ((encoding & 0x7) > DW_EH_PE_udata8) | ||||
|     return false; | ||||
|   if ((encoding & 0x70) > DW_EH_PE_funcrel) | ||||
|     return false; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const { | ||||
|   switch (encoding & 0x70) { | ||||
|     case DW_EH_PE_absptr:  return true; | ||||
|     case DW_EH_PE_pcrel:   return have_section_base_; | ||||
|     case DW_EH_PE_textrel: return have_text_base_; | ||||
|     case DW_EH_PE_datarel: return have_data_base_; | ||||
|     case DW_EH_PE_funcrel: return have_function_base_; | ||||
|     default:               return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| uint64 ByteReader::ReadEncodedPointer(const char *buffer, | ||||
|                                       DwarfPointerEncoding encoding, | ||||
|                                       size_t *len) const { | ||||
|   // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't | ||||
|   // see it here. | ||||
|   assert(encoding != DW_EH_PE_omit); | ||||
|  | ||||
|   // The Linux Standards Base 4.0 does not make this clear, but the | ||||
|   // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c) | ||||
|   // agree that aligned pointers are always absolute, machine-sized, | ||||
|   // machine-signed pointers. | ||||
|   if (encoding == DW_EH_PE_aligned) { | ||||
|     assert(have_section_base_); | ||||
|  | ||||
|     // We don't need to align BUFFER in *our* address space. Rather, we | ||||
|     // need to find the next position in our buffer that would be aligned | ||||
|     // when the .eh_frame section the buffer contains is loaded into the | ||||
|     // program's memory. So align assuming that buffer_base_ gets loaded at | ||||
|     // address section_base_, where section_base_ itself may or may not be | ||||
|     // aligned. | ||||
|  | ||||
|     // First, find the offset to START from the closest prior aligned | ||||
|     // address. | ||||
|     uint64 skew = section_base_ & (AddressSize() - 1); | ||||
|     // Now find the offset from that aligned address to buffer. | ||||
|     uint64 offset = skew + (buffer - buffer_base_); | ||||
|     // Round up to the next boundary. | ||||
|     uint64 aligned = (offset + AddressSize() - 1) & -AddressSize(); | ||||
|     // Convert back to a pointer. | ||||
|     const char *aligned_buffer = buffer_base_ + (aligned - skew); | ||||
|     // Finally, store the length and actually fetch the pointer. | ||||
|     *len = aligned_buffer - buffer + AddressSize(); | ||||
|     return ReadAddress(aligned_buffer); | ||||
|   } | ||||
|  | ||||
|   // Extract the value first, ignoring whether it's a pointer or an | ||||
|   // offset relative to some base. | ||||
|   uint64 offset; | ||||
|   switch (encoding & 0x0f) { | ||||
|     case DW_EH_PE_absptr: | ||||
|       // DW_EH_PE_absptr is weird, as it is used as a meaningful value for | ||||
|       // both the high and low nybble of encoding bytes. When it appears in | ||||
|       // the high nybble, it means that the pointer is absolute, not an | ||||
|       // offset from some base address. When it appears in the low nybble, | ||||
|       // as here, it means that the pointer is stored as a normal | ||||
|       // machine-sized and machine-signed address. A low nybble of | ||||
|       // DW_EH_PE_absptr does not imply that the pointer is absolute; it is | ||||
|       // correct for us to treat the value as an offset from a base address | ||||
|       // if the upper nybble is not DW_EH_PE_absptr. | ||||
|       offset = ReadAddress(buffer); | ||||
|       *len = AddressSize(); | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_uleb128: | ||||
|       offset = ReadUnsignedLEB128(buffer, len); | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_udata2: | ||||
|       offset = ReadTwoBytes(buffer); | ||||
|       *len = 2; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_udata4: | ||||
|       offset = ReadFourBytes(buffer); | ||||
|       *len = 4; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_udata8: | ||||
|       offset = ReadEightBytes(buffer); | ||||
|       *len = 8; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_sleb128: | ||||
|       offset = ReadSignedLEB128(buffer, len); | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_sdata2: | ||||
|       offset = ReadTwoBytes(buffer); | ||||
|       // Sign-extend from 16 bits. | ||||
|       offset = (offset ^ 0x8000) - 0x8000; | ||||
|       *len = 2; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_sdata4: | ||||
|       offset = ReadFourBytes(buffer); | ||||
|       // Sign-extend from 32 bits. | ||||
|       offset = (offset ^ 0x80000000ULL) - 0x80000000ULL; | ||||
|       *len = 4; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_sdata8: | ||||
|       // No need to sign-extend; this is the full width of our type. | ||||
|       offset = ReadEightBytes(buffer); | ||||
|       *len = 8; | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       abort(); | ||||
|   } | ||||
|  | ||||
|   // Find the appropriate base address. | ||||
|   uint64 base; | ||||
|   switch (encoding & 0x70) { | ||||
|     case DW_EH_PE_absptr: | ||||
|       base = 0; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_pcrel: | ||||
|       assert(have_section_base_); | ||||
|       base = section_base_ + (buffer - buffer_base_); | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_textrel: | ||||
|       assert(have_text_base_); | ||||
|       base = text_base_; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_datarel: | ||||
|       assert(have_data_base_); | ||||
|       base = data_base_; | ||||
|       break; | ||||
|  | ||||
|     case DW_EH_PE_funcrel: | ||||
|       assert(have_function_base_); | ||||
|       base = function_base_; | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       abort(); | ||||
|   } | ||||
|  | ||||
|   uint64 pointer = base + offset; | ||||
|  | ||||
|   // Remove inappropriate upper bits. | ||||
|   if (AddressSize() == 4) | ||||
|     pointer = pointer & 0xffffffff; | ||||
|   else | ||||
|     assert(AddressSize() == sizeof(uint64)); | ||||
|  | ||||
|   return pointer; | ||||
| } | ||||
|  | ||||
| }  // namespace dwarf2reader | ||||
							
								
								
									
										310
									
								
								sdk/breakpad/common/dwarf/bytereader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										310
									
								
								sdk/breakpad/common/dwarf/bytereader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,310 @@ | ||||
| // -*- mode: C++ -*- | ||||
|  | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef COMMON_DWARF_BYTEREADER_H__ | ||||
| #define COMMON_DWARF_BYTEREADER_H__ | ||||
|  | ||||
| #include <string> | ||||
| #include "common/dwarf/types.h" | ||||
| #include "common/dwarf/dwarf2enums.h" | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| // We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN | ||||
| // because it conflicts with a macro | ||||
| enum Endianness { | ||||
|   ENDIANNESS_BIG, | ||||
|   ENDIANNESS_LITTLE | ||||
| }; | ||||
|  | ||||
| // A ByteReader knows how to read single- and multi-byte values of | ||||
| // various endiannesses, sizes, and encodings, as used in DWARF | ||||
| // debugging information and Linux C++ exception handling data. | ||||
| class ByteReader { | ||||
|  public: | ||||
|   // Construct a ByteReader capable of reading one-, two-, four-, and | ||||
|   // eight-byte values according to ENDIANNESS, absolute machine-sized | ||||
|   // addresses, DWARF-style "initial length" values, signed and | ||||
|   // unsigned LEB128 numbers, and Linux C++ exception handling data's | ||||
|   // encoded pointers. | ||||
|   explicit ByteReader(enum Endianness endianness); | ||||
|   virtual ~ByteReader(); | ||||
|  | ||||
|   // Read a single byte from BUFFER and return it as an unsigned 8 bit | ||||
|   // number. | ||||
|   uint8 ReadOneByte(const char* buffer) const; | ||||
|  | ||||
|   // Read two bytes from BUFFER and return them as an unsigned 16 bit | ||||
|   // number, using this ByteReader's endianness. | ||||
|   uint16 ReadTwoBytes(const char* buffer) const; | ||||
|  | ||||
|   // Read four bytes from BUFFER and return them as an unsigned 32 bit | ||||
|   // number, using this ByteReader's endianness. This function returns | ||||
|   // a uint64 so that it is compatible with ReadAddress and | ||||
|   // ReadOffset. The number it returns will never be outside the range | ||||
|   // of an unsigned 32 bit integer. | ||||
|   uint64 ReadFourBytes(const char* buffer) const; | ||||
|  | ||||
|   // Read eight bytes from BUFFER and return them as an unsigned 64 | ||||
|   // bit number, using this ByteReader's endianness. | ||||
|   uint64 ReadEightBytes(const char* buffer) const; | ||||
|  | ||||
|   // Read an unsigned LEB128 (Little Endian Base 128) number from | ||||
|   // BUFFER and return it as an unsigned 64 bit integer. Set LEN to | ||||
|   // the number of bytes read. | ||||
|   // | ||||
|   // The unsigned LEB128 representation of an integer N is a variable | ||||
|   // number of bytes: | ||||
|   // | ||||
|   // - If N is between 0 and 0x7f, then its unsigned LEB128 | ||||
|   //   representation is a single byte whose value is N. | ||||
|   // | ||||
|   // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) | | ||||
|   //   0x80, followed by the unsigned LEB128 representation of N / | ||||
|   //   128, rounded towards negative infinity. | ||||
|   // | ||||
|   // In other words, we break VALUE into groups of seven bits, put | ||||
|   // them in little-endian order, and then write them as eight-bit | ||||
|   // bytes with the high bit on all but the last. | ||||
|   uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const; | ||||
|  | ||||
|   // Read a signed LEB128 number from BUFFER and return it as an | ||||
|   // signed 64 bit integer. Set LEN to the number of bytes read. | ||||
|   // | ||||
|   // The signed LEB128 representation of an integer N is a variable | ||||
|   // number of bytes: | ||||
|   // | ||||
|   // - If N is between -0x40 and 0x3f, then its signed LEB128 | ||||
|   //   representation is a single byte whose value is N in two's | ||||
|   //   complement. | ||||
|   // | ||||
|   // - Otherwise, its signed LEB128 representation is (N & 0x7f) | | ||||
|   //   0x80, followed by the signed LEB128 representation of N / 128, | ||||
|   //   rounded towards negative infinity. | ||||
|   // | ||||
|   // In other words, we break VALUE into groups of seven bits, put | ||||
|   // them in little-endian order, and then write them as eight-bit | ||||
|   // bytes with the high bit on all but the last. | ||||
|   int64 ReadSignedLEB128(const char* buffer, size_t* len) const; | ||||
|  | ||||
|   // Indicate that addresses on this architecture are SIZE bytes long. SIZE | ||||
|   // must be either 4 or 8. (DWARF allows addresses to be any number of | ||||
|   // bytes in length from 1 to 255, but we only support 32- and 64-bit | ||||
|   // addresses at the moment.) You must call this before using the | ||||
|   // ReadAddress member function. | ||||
|   // | ||||
|   // For data in a .debug_info section, or something that .debug_info | ||||
|   // refers to like line number or macro data, the compilation unit | ||||
|   // header's address_size field indicates the address size to use. Call | ||||
|   // frame information doesn't indicate its address size (a shortcoming of | ||||
|   // the spec); you must supply the appropriate size based on the | ||||
|   // architecture of the target machine. | ||||
|   void SetAddressSize(uint8 size); | ||||
|  | ||||
|   // Return the current address size, in bytes. This is either 4, | ||||
|   // indicating 32-bit addresses, or 8, indicating 64-bit addresses. | ||||
|   uint8 AddressSize() const { return address_size_; } | ||||
|  | ||||
|   // Read an address from BUFFER and return it as an unsigned 64 bit | ||||
|   // integer, respecting this ByteReader's endianness and address size. You | ||||
|   // must call SetAddressSize before calling this function. | ||||
|   uint64 ReadAddress(const char* buffer) const; | ||||
|  | ||||
|   // DWARF actually defines two slightly different formats: 32-bit DWARF | ||||
|   // and 64-bit DWARF. This is *not* related to the size of registers or | ||||
|   // addresses on the target machine; it refers only to the size of section | ||||
|   // offsets and data lengths appearing in the DWARF data. One only needs | ||||
|   // 64-bit DWARF when the debugging data itself is larger than 4GiB. | ||||
|   // 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the | ||||
|   // debugging data itself is very large. | ||||
|   // | ||||
|   // DWARF information identifies itself as 32-bit or 64-bit DWARF: each | ||||
|   // compilation unit and call frame information entry begins with an | ||||
|   // "initial length" field, which, in addition to giving the length of the | ||||
|   // data, also indicates the size of section offsets and lengths appearing | ||||
|   // in that data. The ReadInitialLength member function, below, reads an | ||||
|   // initial length and sets the ByteReader's offset size as a side effect. | ||||
|   // Thus, in the normal process of reading DWARF data, the appropriate | ||||
|   // offset size is set automatically. So, you should only need to call | ||||
|   // SetOffsetSize if you are using the same ByteReader to jump from the | ||||
|   // midst of one block of DWARF data into another. | ||||
|  | ||||
|   // Read a DWARF "initial length" field from START, and return it as | ||||
|   // an unsigned 64 bit integer, respecting this ByteReader's | ||||
|   // endianness. Set *LEN to the length of the initial length in | ||||
|   // bytes, either four or twelve. As a side effect, set this | ||||
|   // ByteReader's offset size to either 4 (if we see a 32-bit DWARF | ||||
|   // initial length) or 8 (if we see a 64-bit DWARF initial length). | ||||
|   // | ||||
|   // A DWARF initial length is either: | ||||
|   // | ||||
|   // - a byte count stored as an unsigned 32-bit value less than | ||||
|   //   0xffffff00, indicating that the data whose length is being | ||||
|   //   measured uses the 32-bit DWARF format, or | ||||
|   // | ||||
|   // - The 32-bit value 0xffffffff, followed by a 64-bit byte count, | ||||
|   //   indicating that the data whose length is being measured uses | ||||
|   //   the 64-bit DWARF format. | ||||
|   uint64 ReadInitialLength(const char* start, size_t* len); | ||||
|  | ||||
|   // Read an offset from BUFFER and return it as an unsigned 64 bit | ||||
|   // integer, respecting the ByteReader's endianness. In 32-bit DWARF, the | ||||
|   // offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes | ||||
|   // long. You must call ReadInitialLength or SetOffsetSize before calling | ||||
|   // this function; see the comments above for details. | ||||
|   uint64 ReadOffset(const char* buffer) const; | ||||
|  | ||||
|   // Return the current offset size, in bytes. | ||||
|   // A return value of 4 indicates that we are reading 32-bit DWARF. | ||||
|   // A return value of 8 indicates that we are reading 64-bit DWARF. | ||||
|   uint8 OffsetSize() const { return offset_size_; } | ||||
|  | ||||
|   // Indicate that section offsets and lengths are SIZE bytes long. SIZE | ||||
|   // must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF). | ||||
|   // Usually, you should not call this function yourself; instead, let a | ||||
|   // call to ReadInitialLength establish the data's offset size | ||||
|   // automatically. | ||||
|   void SetOffsetSize(uint8 size); | ||||
|  | ||||
|   // The Linux C++ ABI uses a variant of DWARF call frame information | ||||
|   // for exception handling. This data is included in the program's | ||||
|   // address space as the ".eh_frame" section, and intepreted at | ||||
|   // runtime to walk the stack, find exception handlers, and run | ||||
|   // cleanup code. The format is mostly the same as DWARF CFI, with | ||||
|   // some adjustments made to provide the additional | ||||
|   // exception-handling data, and to make the data easier to work with | ||||
|   // in memory --- for example, to allow it to be placed in read-only | ||||
|   // memory even when describing position-independent code. | ||||
|   // | ||||
|   // In particular, exception handling data can select a number of | ||||
|   // different encodings for pointers that appear in the data, as | ||||
|   // described by the DwarfPointerEncoding enum. There are actually | ||||
|   // four axes(!) to the encoding: | ||||
|   // | ||||
|   // - The pointer size: pointers can be 2, 4, or 8 bytes long, or use | ||||
|   //   the DWARF LEB128 encoding. | ||||
|   // | ||||
|   // - The pointer's signedness: pointers can be signed or unsigned. | ||||
|   // | ||||
|   // - The pointer's base address: the data stored in the exception | ||||
|   //   handling data can be the actual address (that is, an absolute | ||||
|   //   pointer), or relative to one of a number of different base | ||||
|   //   addreses --- including that of the encoded pointer itself, for | ||||
|   //   a form of "pc-relative" addressing. | ||||
|   // | ||||
|   // - The pointer may be indirect: it may be the address where the | ||||
|   //   true pointer is stored. (This is used to refer to things via | ||||
|   //   global offset table entries, program linkage table entries, or | ||||
|   //   other tricks used in position-independent code.) | ||||
|   // | ||||
|   // There are also two options that fall outside that matrix | ||||
|   // altogether: the pointer may be omitted, or it may have padding to | ||||
|   // align it on an appropriate address boundary. (That last option | ||||
|   // may seem like it should be just another axis, but it is not.) | ||||
|  | ||||
|   // Indicate that the exception handling data is loaded starting at | ||||
|   // SECTION_BASE, and that the start of its buffer in our own memory | ||||
|   // is BUFFER_BASE. This allows us to find the address that a given | ||||
|   // byte in our buffer would have when loaded into the program the | ||||
|   // data describes. We need this to resolve DW_EH_PE_pcrel pointers. | ||||
|   void SetCFIDataBase(uint64 section_base, const char *buffer_base); | ||||
|  | ||||
|   // Indicate that the base address of the program's ".text" section | ||||
|   // is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers. | ||||
|   void SetTextBase(uint64 text_base); | ||||
|  | ||||
|   // Indicate that the base address for DW_EH_PE_datarel pointers is | ||||
|   // DATA_BASE. The proper value depends on the ABI; it is usually the | ||||
|   // address of the global offset table, held in a designated register in | ||||
|   // position-independent code. You will need to look at the startup code | ||||
|   // for the target system to be sure. I tried; my eyes bled. | ||||
|   void SetDataBase(uint64 data_base); | ||||
|  | ||||
|   // Indicate that the base address for the FDE we are processing is | ||||
|   // FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel | ||||
|   // pointers. (This encoding does not seem to be used by the GNU | ||||
|   // toolchain.) | ||||
|   void SetFunctionBase(uint64 function_base); | ||||
|  | ||||
|   // Indicate that we are no longer processing any FDE, so any use of | ||||
|   // a DW_EH_PE_funcrel encoding is an error. | ||||
|   void ClearFunctionBase(); | ||||
|  | ||||
|   // Return true if ENCODING is a valid pointer encoding. | ||||
|   bool ValidEncoding(DwarfPointerEncoding encoding) const; | ||||
|  | ||||
|   // Return true if we have all the information we need to read a | ||||
|   // pointer that uses ENCODING. This checks that the appropriate | ||||
|   // SetFooBase function for ENCODING has been called. | ||||
|   bool UsableEncoding(DwarfPointerEncoding encoding) const; | ||||
|  | ||||
|   // Read an encoded pointer from BUFFER using ENCODING; return the | ||||
|   // absolute address it represents, and set *LEN to the pointer's | ||||
|   // length in bytes, including any padding for aligned pointers. | ||||
|   // | ||||
|   // This function calls 'abort' if ENCODING is invalid or refers to a | ||||
|   // base address this reader hasn't been given, so you should check | ||||
|   // with ValidEncoding and UsableEncoding first if you would rather | ||||
|   // die in a more helpful way. | ||||
|   uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding, | ||||
|                             size_t *len) const; | ||||
|  | ||||
|  private: | ||||
|  | ||||
|   // Function pointer type for our address and offset readers. | ||||
|   typedef uint64 (ByteReader::*AddressReader)(const char*) const; | ||||
|  | ||||
|   // Read an offset from BUFFER and return it as an unsigned 64 bit | ||||
|   // integer.  DWARF2/3 define offsets as either 4 or 8 bytes, | ||||
|   // generally depending on the amount of DWARF2/3 info present. | ||||
|   // This function pointer gets set by SetOffsetSize. | ||||
|   AddressReader offset_reader_; | ||||
|  | ||||
|   // Read an address from BUFFER and return it as an unsigned 64 bit | ||||
|   // integer.  DWARF2/3 allow addresses to be any size from 0-255 | ||||
|   // bytes currently.  Internally we support 4 and 8 byte addresses, | ||||
|   // and will CHECK on anything else. | ||||
|   // This function pointer gets set by SetAddressSize. | ||||
|   AddressReader address_reader_; | ||||
|  | ||||
|   Endianness endian_; | ||||
|   uint8 address_size_; | ||||
|   uint8 offset_size_; | ||||
|  | ||||
|   // Base addresses for Linux C++ exception handling data's encoded pointers. | ||||
|   bool have_section_base_, have_text_base_, have_data_base_; | ||||
|   bool have_function_base_; | ||||
|   uint64 section_base_, text_base_, data_base_, function_base_; | ||||
|   const char *buffer_base_; | ||||
| }; | ||||
|  | ||||
| }  // namespace dwarf2reader | ||||
|  | ||||
| #endif  // COMMON_DWARF_BYTEREADER_H__ | ||||
							
								
								
									
										697
									
								
								sdk/breakpad/common/dwarf/bytereader_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										697
									
								
								sdk/breakpad/common/dwarf/bytereader_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,697 @@ | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/dwarf/bytereader.h" | ||||
| #include "common/dwarf/bytereader-inl.h" | ||||
| #include "common/dwarf/cfi_assembler.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using dwarf2reader::ByteReader; | ||||
| using dwarf2reader::DwarfPointerEncoding; | ||||
| using dwarf2reader::ENDIANNESS_BIG; | ||||
| using dwarf2reader::ENDIANNESS_LITTLE; | ||||
| using google_breakpad::CFISection; | ||||
| using google_breakpad::test_assembler::Label; | ||||
| using google_breakpad::test_assembler::kBigEndian; | ||||
| using google_breakpad::test_assembler::kLittleEndian; | ||||
| using google_breakpad::test_assembler::Section; | ||||
| using testing::Test; | ||||
|  | ||||
| struct ReaderFixture { | ||||
|   string contents; | ||||
|   size_t pointer_size; | ||||
| }; | ||||
|  | ||||
| class Reader: public ReaderFixture, public Test { }; | ||||
| class ReaderDeathTest: public ReaderFixture, public Test { }; | ||||
|  | ||||
| TEST_F(Reader, SimpleConstructor) { | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(4); | ||||
|   CFISection section(kBigEndian, 4); | ||||
|   section | ||||
|     .D8(0xc0) | ||||
|     .D16(0xcf0d) | ||||
|     .D32(0x96fdd219) | ||||
|     .D64(0xbbf55fef0825f117ULL) | ||||
|     .ULEB128(0xa0927048ba8121afULL) | ||||
|     .LEB128(-0x4f337badf4483f83LL) | ||||
|     .D32(0xfec319c9); | ||||
|   ASSERT_TRUE(section.GetContents(&contents)); | ||||
|   const char *data = contents.data(); | ||||
|   EXPECT_EQ(0xc0U, reader.ReadOneByte(data)); | ||||
|   EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1)); | ||||
|   EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3)); | ||||
|   EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7)); | ||||
|   size_t leb128_size; | ||||
|   EXPECT_EQ(0xa0927048ba8121afULL, | ||||
|             reader.ReadUnsignedLEB128(data + 15, &leb128_size)); | ||||
|   EXPECT_EQ(10U, leb128_size); | ||||
|   EXPECT_EQ(-0x4f337badf4483f83LL, | ||||
|             reader.ReadSignedLEB128(data + 25, &leb128_size)); | ||||
|   EXPECT_EQ(10U, leb128_size); | ||||
|   EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35)); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, ValidEncodings) { | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_pcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_textrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_datarel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_absptr | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_uleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata2 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata4 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_udata8 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sleb128 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata2 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata4 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|   EXPECT_TRUE(reader.ValidEncoding( | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | | ||||
|                            dwarf2reader::DW_EH_PE_sdata8 | | ||||
|                            dwarf2reader::DW_EH_PE_funcrel))); | ||||
|  | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0))); | ||||
|   EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0))); | ||||
| } | ||||
|  | ||||
| TEST_F(ReaderDeathTest, DW_EH_PE_omit) { | ||||
|   static const char data[1] = { 42 }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(4); | ||||
|   EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit, | ||||
|                                          &pointer_size), | ||||
|                "encoding != DW_EH_PE_omit"); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_absptr4) { | ||||
|   static const char data[] = { 0x27, 0x57, 0xea, 0x40 }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(4); | ||||
|   EXPECT_EQ(0x40ea5727U, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(4U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_absptr8) { | ||||
|   static const char data[] = { | ||||
|     0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50 | ||||
|   }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(8); | ||||
|   EXPECT_EQ(0x010598c240ea5727ULL, | ||||
|             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(8U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_uleb128) { | ||||
|   static const char data[] = { 0x81, 0x84, 0x4c }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(4); | ||||
|   EXPECT_EQ(0x130201U, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(3U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_udata2) { | ||||
|   static const char data[] = { 0xf4, 0x8d }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(4); | ||||
|   EXPECT_EQ(0xf48dU, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(2U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_udata4) { | ||||
|   static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(8); | ||||
|   EXPECT_EQ(0xa5628f8b, | ||||
|             reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(4U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_udata8Addr8) { | ||||
|   static const char data[] = { | ||||
|     0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe | ||||
|   }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(8); | ||||
|   EXPECT_EQ(0x8fed199f69047304ULL, | ||||
|             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, | ||||
|                                         &pointer_size)); | ||||
|   EXPECT_EQ(8U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_udata8Addr4) { | ||||
|   static const char data[] = { | ||||
|     0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe | ||||
|   }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(4); | ||||
|   EXPECT_EQ(0x69047304ULL, | ||||
|             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, | ||||
|                                         &pointer_size)); | ||||
|   EXPECT_EQ(8U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_sleb128) { | ||||
|   static const char data[] = { 0x42, 0xff, 0xfb, 0x73 }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(4); | ||||
|   EXPECT_EQ(-0x030201U & 0xffffffff, | ||||
|             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128, | ||||
|                                         &pointer_size)); | ||||
|   EXPECT_EQ(3U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_sdata2) { | ||||
|   static const char data[] = { 0xb9, 0xbf }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(8); | ||||
|   EXPECT_EQ(0xffffffffffffbfb9ULL, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2, | ||||
|                                         &pointer_size)); | ||||
|   EXPECT_EQ(2U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_sdata4) { | ||||
|   static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(8); | ||||
|   EXPECT_EQ(0xffffffffadc2b8f2ULL, | ||||
|             reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4, | ||||
|                                         &pointer_size)); | ||||
|   EXPECT_EQ(4U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_sdata8) { | ||||
|   static const char data[] = { | ||||
|     0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87 | ||||
|   }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(8); | ||||
|   EXPECT_EQ(0x87269b0ce0795766ULL, | ||||
|             reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8, | ||||
|                                         &pointer_size)); | ||||
|   EXPECT_EQ(8U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_pcrel) { | ||||
|   static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(4); | ||||
|   DwarfPointerEncoding encoding = | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel | ||||
|                            | dwarf2reader::DW_EH_PE_absptr); | ||||
|   reader.SetCFIDataBase(0x89951377, data); | ||||
|   EXPECT_EQ(0x89951377 + 3 + 0x14c8c402, | ||||
|             reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); | ||||
|   EXPECT_EQ(4U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_textrel) { | ||||
|   static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e }; | ||||
|   ByteReader reader(ENDIANNESS_LITTLE); | ||||
|   reader.SetAddressSize(4); | ||||
|   reader.SetTextBase(0xb91beaf0); | ||||
|   DwarfPointerEncoding encoding = | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel | ||||
|                            | dwarf2reader::DW_EH_PE_sdata2); | ||||
|   EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff, | ||||
|             reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); | ||||
|   EXPECT_EQ(2U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_datarel) { | ||||
|   static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(8); | ||||
|   reader.SetDataBase(0xbef308bd25ce74f0ULL); | ||||
|   DwarfPointerEncoding encoding = | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel | ||||
|                            | dwarf2reader::DW_EH_PE_sleb128); | ||||
|   EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL, | ||||
|             reader.ReadEncodedPointer(data + 2, encoding, &pointer_size)); | ||||
|   EXPECT_EQ(3U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Reader, DW_EH_PE_funcrel) { | ||||
|   static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetAddressSize(4); | ||||
|   reader.SetFunctionBase(0x823c3520); | ||||
|   DwarfPointerEncoding encoding = | ||||
|       DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel | ||||
|                            | dwarf2reader::DW_EH_PE_udata2); | ||||
|   EXPECT_EQ(0x823c3520 + 0xd148, | ||||
|             reader.ReadEncodedPointer(data + 5, encoding, &pointer_size)); | ||||
|   EXPECT_EQ(2U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST(UsableBase, CFI) { | ||||
|   static const char data[1] = { 0x42 }; | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetCFIDataBase(0xb31cbd20, data); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||
| } | ||||
|  | ||||
| TEST(UsableBase, Text) { | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetTextBase(0xa899ccb9); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||
| } | ||||
|  | ||||
| TEST(UsableBase, Data) { | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetDataBase(0xf7b10bcd); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||
| } | ||||
|  | ||||
| TEST(UsableBase, Function) { | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetFunctionBase(0xc2c0ed81); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||
| } | ||||
|  | ||||
| TEST(UsableBase, ClearFunction) { | ||||
|   ByteReader reader(ENDIANNESS_BIG); | ||||
|   reader.SetFunctionBase(0xc2c0ed81); | ||||
|   reader.ClearFunctionBase(); | ||||
|   EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); | ||||
|   EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); | ||||
| } | ||||
|  | ||||
| struct AlignedFixture { | ||||
|   AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); } | ||||
|   static const char data[10]; | ||||
|   ByteReader reader; | ||||
|   size_t pointer_size; | ||||
| }; | ||||
|    | ||||
| const char AlignedFixture::data[10] = { | ||||
|   0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b | ||||
| }; | ||||
|  | ||||
| class Aligned: public AlignedFixture, public Test { }; | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned0) { | ||||
|   reader.SetCFIDataBase(0xb440305c, data); | ||||
|   EXPECT_EQ(0xfe6e93d8U, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(4U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned1) { | ||||
|   reader.SetCFIDataBase(0xb440305d, data); | ||||
|   EXPECT_EQ(0xd834d51cU, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(7U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned2) { | ||||
|   reader.SetCFIDataBase(0xb440305e, data); | ||||
|   EXPECT_EQ(0x93d834d5U, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(6U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned3) { | ||||
|   reader.SetCFIDataBase(0xb440305f, data); | ||||
|   EXPECT_EQ(0x6e93d834U, | ||||
|             reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(5U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned11) { | ||||
|   reader.SetCFIDataBase(0xb4403061, data); | ||||
|   EXPECT_EQ(0xd834d51cU, | ||||
|             reader.ReadEncodedPointer(data + 1, | ||||
|                                       dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(6U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned30) { | ||||
|   reader.SetCFIDataBase(0xb4403063, data); | ||||
|   EXPECT_EQ(0x6e93d834U, | ||||
|             reader.ReadEncodedPointer(data + 1, | ||||
|                                       dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(4U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned23) { | ||||
|   reader.SetCFIDataBase(0xb4403062, data); | ||||
|   EXPECT_EQ(0x1cd3ac2bU, | ||||
|             reader.ReadEncodedPointer(data + 3, | ||||
|                                       dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(7U, pointer_size); | ||||
| } | ||||
|  | ||||
| TEST_F(Aligned, DW_EH_PE_aligned03) { | ||||
|   reader.SetCFIDataBase(0xb4403064, data); | ||||
|   EXPECT_EQ(0x34d51cd3U, | ||||
|             reader.ReadEncodedPointer(data + 3, | ||||
|                                       dwarf2reader::DW_EH_PE_aligned, | ||||
|                                       &pointer_size)); | ||||
|   EXPECT_EQ(5U, pointer_size); | ||||
| }   | ||||
							
								
								
									
										198
									
								
								sdk/breakpad/common/dwarf/cfi_assembler.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								sdk/breakpad/common/dwarf/cfi_assembler.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // cfi_assembler.cc: Implementation of google_breakpad::CFISection class. | ||||
| // See cfi_assembler.h for details. | ||||
|  | ||||
| #include "common/dwarf/cfi_assembler.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| using dwarf2reader::DwarfPointerEncoding; | ||||
|    | ||||
| CFISection &CFISection::CIEHeader(uint64_t code_alignment_factor, | ||||
|                                   int data_alignment_factor, | ||||
|                                   unsigned return_address_register, | ||||
|                                   uint8_t version, | ||||
|                                   const string &augmentation, | ||||
|                                   bool dwarf64) { | ||||
|   assert(!entry_length_); | ||||
|   entry_length_ = new PendingLength(); | ||||
|   in_fde_ = false; | ||||
|  | ||||
|   if (dwarf64) { | ||||
|     D32(kDwarf64InitialLengthMarker); | ||||
|     D64(entry_length_->length); | ||||
|     entry_length_->start = Here(); | ||||
|     D64(eh_frame_ ? kEHFrame64CIEIdentifier : kDwarf64CIEIdentifier); | ||||
|   } else { | ||||
|     D32(entry_length_->length); | ||||
|     entry_length_->start = Here(); | ||||
|     D32(eh_frame_ ? kEHFrame32CIEIdentifier : kDwarf32CIEIdentifier); | ||||
|   } | ||||
|   D8(version); | ||||
|   AppendCString(augmentation); | ||||
|   ULEB128(code_alignment_factor); | ||||
|   LEB128(data_alignment_factor); | ||||
|   if (version == 1) | ||||
|     D8(return_address_register); | ||||
|   else | ||||
|     ULEB128(return_address_register); | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| CFISection &CFISection::FDEHeader(Label cie_pointer, | ||||
|                                   uint64_t initial_location, | ||||
|                                   uint64_t address_range, | ||||
|                                   bool dwarf64) { | ||||
|   assert(!entry_length_); | ||||
|   entry_length_ = new PendingLength(); | ||||
|   in_fde_ = true; | ||||
|   fde_start_address_ = initial_location; | ||||
|  | ||||
|   if (dwarf64) { | ||||
|     D32(0xffffffff); | ||||
|     D64(entry_length_->length); | ||||
|     entry_length_->start = Here(); | ||||
|     if (eh_frame_) | ||||
|       D64(Here() - cie_pointer); | ||||
|     else | ||||
|       D64(cie_pointer); | ||||
|   } else { | ||||
|     D32(entry_length_->length); | ||||
|     entry_length_->start = Here(); | ||||
|     if (eh_frame_) | ||||
|       D32(Here() - cie_pointer); | ||||
|     else | ||||
|       D32(cie_pointer); | ||||
|   } | ||||
|   EncodedPointer(initial_location); | ||||
|   // The FDE length in an .eh_frame section uses the same encoding as the | ||||
|   // initial location, but ignores the base address (selected by the upper | ||||
|   // nybble of the encoding), as it's a length, not an address that can be | ||||
|   // made relative. | ||||
|   EncodedPointer(address_range, | ||||
|                  DwarfPointerEncoding(pointer_encoding_ & 0x0f)); | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| CFISection &CFISection::FinishEntry() { | ||||
|   assert(entry_length_); | ||||
|   Align(address_size_, dwarf2reader::DW_CFA_nop); | ||||
|   entry_length_->length = Here() - entry_length_->start; | ||||
|   delete entry_length_; | ||||
|   entry_length_ = NULL; | ||||
|   in_fde_ = false; | ||||
|   return *this; | ||||
| } | ||||
|  | ||||
| CFISection &CFISection::EncodedPointer(uint64_t address, | ||||
|                                        DwarfPointerEncoding encoding, | ||||
|                                        const EncodedPointerBases &bases) { | ||||
|   // Omitted data is extremely easy to emit. | ||||
|   if (encoding == dwarf2reader::DW_EH_PE_omit) | ||||
|     return *this; | ||||
|  | ||||
|   // If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume | ||||
|   // that ADDRESS is the address at which the pointer is stored --- in | ||||
|   // other words, that bit has no effect on how we write the pointer. | ||||
|   encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect); | ||||
|  | ||||
|   // Find the base address to which this pointer is relative. The upper | ||||
|   // nybble of the encoding specifies this. | ||||
|   uint64_t base; | ||||
|   switch (encoding & 0xf0) { | ||||
|     case dwarf2reader::DW_EH_PE_absptr:  base = 0;                  break; | ||||
|     case dwarf2reader::DW_EH_PE_pcrel:   base = bases.cfi + Size(); break; | ||||
|     case dwarf2reader::DW_EH_PE_textrel: base = bases.text;         break; | ||||
|     case dwarf2reader::DW_EH_PE_datarel: base = bases.data;         break; | ||||
|     case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break; | ||||
|     case dwarf2reader::DW_EH_PE_aligned: base = 0;                  break; | ||||
|     default: abort(); | ||||
|   }; | ||||
|  | ||||
|   // Make ADDRESS relative. Yes, this is appropriate even for "absptr" | ||||
|   // values; see gcc/unwind-pe.h. | ||||
|   address -= base; | ||||
|  | ||||
|   // Align the pointer, if required. | ||||
|   if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned) | ||||
|     Align(AddressSize()); | ||||
|  | ||||
|   // Append ADDRESS to this section in the appropriate form. For the | ||||
|   // fixed-width forms, we don't need to differentiate between signed and | ||||
|   // unsigned encodings, because ADDRESS has already been extended to 64 | ||||
|   // bits before it was passed to us. | ||||
|   switch (encoding & 0x0f) { | ||||
|     case dwarf2reader::DW_EH_PE_absptr: | ||||
|       Address(address); | ||||
|       break; | ||||
|  | ||||
|     case dwarf2reader::DW_EH_PE_uleb128: | ||||
|       ULEB128(address); | ||||
|       break; | ||||
|  | ||||
|     case dwarf2reader::DW_EH_PE_sleb128: | ||||
|       LEB128(address); | ||||
|       break; | ||||
|  | ||||
|     case dwarf2reader::DW_EH_PE_udata2: | ||||
|     case dwarf2reader::DW_EH_PE_sdata2: | ||||
|       D16(address); | ||||
|       break; | ||||
|  | ||||
|     case dwarf2reader::DW_EH_PE_udata4: | ||||
|     case dwarf2reader::DW_EH_PE_sdata4: | ||||
|       D32(address); | ||||
|       break; | ||||
|  | ||||
|     case dwarf2reader::DW_EH_PE_udata8: | ||||
|     case dwarf2reader::DW_EH_PE_sdata8: | ||||
|       D64(address); | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       abort(); | ||||
|   } | ||||
|  | ||||
|   return *this; | ||||
| }; | ||||
|  | ||||
| const uint32_t CFISection::kDwarf64InitialLengthMarker; | ||||
| const uint32_t CFISection::kDwarf32CIEIdentifier; | ||||
| const uint64_t CFISection::kDwarf64CIEIdentifier; | ||||
| const uint32_t CFISection::kEHFrame32CIEIdentifier; | ||||
| const uint64_t CFISection::kEHFrame64CIEIdentifier; | ||||
|  | ||||
| } // namespace google_breakpad | ||||
							
								
								
									
										269
									
								
								sdk/breakpad/common/dwarf/cfi_assembler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										269
									
								
								sdk/breakpad/common/dwarf/cfi_assembler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,269 @@ | ||||
| // -*- mode: C++ -*- | ||||
|  | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // cfi_assembler.h: Define CFISection, a class for creating properly | ||||
| // (and improperly) formatted DWARF CFI data for unit tests. | ||||
|  | ||||
| #ifndef PROCESSOR_CFI_ASSEMBLER_H_ | ||||
| #define PROCESSOR_CFI_ASSEMBLER_H_ | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/dwarf/dwarf2enums.h" | ||||
| #include "common/test_assembler.h" | ||||
| #include "common/using_std_string.h" | ||||
| #include "google_breakpad/common/breakpad_types.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| using dwarf2reader::DwarfPointerEncoding; | ||||
| using google_breakpad::test_assembler::Endianness; | ||||
| using google_breakpad::test_assembler::Label; | ||||
| using google_breakpad::test_assembler::Section; | ||||
|  | ||||
| class CFISection: public Section { | ||||
|  public: | ||||
|  | ||||
|   // CFI augmentation strings beginning with 'z', defined by the | ||||
|   // Linux/IA-64 C++ ABI, can specify interesting encodings for | ||||
|   // addresses appearing in FDE headers and call frame instructions (and | ||||
|   // for additional fields whose presence the augmentation string | ||||
|   // specifies). In particular, pointers can be specified to be relative | ||||
|   // to various base address: the start of the .text section, the | ||||
|   // location holding the address itself, and so on. These allow the | ||||
|   // frame data to be position-independent even when they live in | ||||
|   // write-protected pages. These variants are specified at the | ||||
|   // following two URLs: | ||||
|   // | ||||
|   // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html | ||||
|   // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html | ||||
|   // | ||||
|   // CFISection leaves the production of well-formed 'z'-augmented CIEs and | ||||
|   // FDEs to the user, but does provide EncodedPointer, to emit | ||||
|   // properly-encoded addresses for a given pointer encoding. | ||||
|   // EncodedPointer uses an instance of this structure to find the base | ||||
|   // addresses it should use; you can establish a default for all encoded | ||||
|   // pointers appended to this section with SetEncodedPointerBases. | ||||
|   struct EncodedPointerBases { | ||||
|     EncodedPointerBases() : cfi(), text(), data() { } | ||||
|  | ||||
|     // The starting address of this CFI section in memory, for | ||||
|     // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data | ||||
|     // that has is loaded into the program's address space. | ||||
|     uint64_t cfi; | ||||
|  | ||||
|     // The starting address of this file's .text section, for DW_EH_PE_textrel. | ||||
|     uint64_t text; | ||||
|  | ||||
|     // The starting address of this file's .got or .eh_frame_hdr section, | ||||
|     // for DW_EH_PE_datarel. | ||||
|     uint64_t data; | ||||
|   }; | ||||
|  | ||||
|   // Create a CFISection whose endianness is ENDIANNESS, and where | ||||
|   // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is | ||||
|   // true, use the .eh_frame format, as described by the Linux | ||||
|   // Standards Base Core Specification, instead of the DWARF CFI | ||||
|   // format. | ||||
|   CFISection(Endianness endianness, size_t address_size, | ||||
|              bool eh_frame = false) | ||||
|       : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), | ||||
|         pointer_encoding_(dwarf2reader::DW_EH_PE_absptr), | ||||
|         encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) { | ||||
|     // The 'start', 'Here', and 'Mark' members of a CFISection all refer | ||||
|     // to section offsets. | ||||
|     start() = 0; | ||||
|   } | ||||
|  | ||||
|   // Return this CFISection's address size. | ||||
|   size_t AddressSize() const { return address_size_; } | ||||
|  | ||||
|   // Return true if this CFISection uses the .eh_frame format, or | ||||
|   // false if it contains ordinary DWARF CFI data. | ||||
|   bool ContainsEHFrame() const { return eh_frame_; } | ||||
|  | ||||
|   // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer. | ||||
|   void SetPointerEncoding(DwarfPointerEncoding encoding) { | ||||
|     pointer_encoding_ = encoding; | ||||
|   } | ||||
|  | ||||
|   // Use the addresses in BASES as the base addresses for encoded | ||||
|   // pointers in subsequent calls to FDEHeader or EncodedPointer. | ||||
|   // This function makes a copy of BASES. | ||||
|   void SetEncodedPointerBases(const EncodedPointerBases &bases) { | ||||
|     encoded_pointer_bases_ = bases; | ||||
|   } | ||||
|  | ||||
|   // Append a Common Information Entry header to this section with the | ||||
|   // given values. If dwarf64 is true, use the 64-bit DWARF initial | ||||
|   // length format for the CIE's initial length. Return a reference to | ||||
|   // this section. You should call FinishEntry after writing the last | ||||
|   // instruction for the CIE. | ||||
|   // | ||||
|   // Before calling this function, you will typically want to use Mark | ||||
|   // or Here to make a label to pass to FDEHeader that refers to this | ||||
|   // CIE's position in the section. | ||||
|   CFISection &CIEHeader(uint64_t code_alignment_factor, | ||||
|                         int data_alignment_factor, | ||||
|                         unsigned return_address_register, | ||||
|                         uint8_t version = 3, | ||||
|                         const string &augmentation = "", | ||||
|                         bool dwarf64 = false); | ||||
|  | ||||
|   // Append a Frame Description Entry header to this section with the | ||||
|   // given values. If dwarf64 is true, use the 64-bit DWARF initial | ||||
|   // length format for the CIE's initial length. Return a reference to | ||||
|   // this section. You should call FinishEntry after writing the last | ||||
|   // instruction for the CIE. | ||||
|   // | ||||
|   // This function doesn't support entries that are longer than | ||||
|   // 0xffffff00 bytes. (The "initial length" is always a 32-bit | ||||
|   // value.) Nor does it support .debug_frame sections longer than | ||||
|   // 0xffffff00 bytes. | ||||
|   CFISection &FDEHeader(Label cie_pointer, | ||||
|                         uint64_t initial_location, | ||||
|                         uint64_t address_range, | ||||
|                         bool dwarf64 = false); | ||||
|  | ||||
|   // Note the current position as the end of the last CIE or FDE we | ||||
|   // started, after padding with DW_CFA_nops for alignment. This | ||||
|   // defines the label representing the entry's length, cited in the | ||||
|   // entry's header. Return a reference to this section. | ||||
|   CFISection &FinishEntry(); | ||||
|  | ||||
|   // Append the contents of BLOCK as a DW_FORM_block value: an | ||||
|   // unsigned LEB128 length, followed by that many bytes of data. | ||||
|   CFISection &Block(const string &block) { | ||||
|     ULEB128(block.size()); | ||||
|     Append(block); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Append ADDRESS to this section, in the appropriate size and | ||||
|   // endianness. Return a reference to this section. | ||||
|   CFISection &Address(uint64_t address) { | ||||
|     Section::Append(endianness(), address_size_, address); | ||||
|     return *this; | ||||
|   } | ||||
|   CFISection &Address(Label address) { | ||||
|     Section::Append(endianness(), address_size_, address); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Append ADDRESS to this section, using ENCODING and BASES. ENCODING | ||||
|   // defaults to this section's default encoding, established by | ||||
|   // SetPointerEncoding. BASES defaults to this section's bases, set by | ||||
|   // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the | ||||
|   // encoding, assume that ADDRESS is where the true address is stored. | ||||
|   // Return a reference to this section. | ||||
|   //  | ||||
|   // (C++ doesn't let me use default arguments here, because I want to | ||||
|   // refer to members of *this in the default argument expression.) | ||||
|   CFISection &EncodedPointer(uint64_t address) { | ||||
|     return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_); | ||||
|   } | ||||
|   CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding) { | ||||
|     return EncodedPointer(address, encoding, encoded_pointer_bases_); | ||||
|   } | ||||
|   CFISection &EncodedPointer(uint64_t address, DwarfPointerEncoding encoding, | ||||
|                              const EncodedPointerBases &bases); | ||||
|  | ||||
|   // Restate some member functions, to keep chaining working nicely. | ||||
|   CFISection &Mark(Label *label)   { Section::Mark(label); return *this; } | ||||
|   CFISection &D8(uint8_t v)       { Section::D8(v);       return *this; } | ||||
|   CFISection &D16(uint16_t v)     { Section::D16(v);      return *this; } | ||||
|   CFISection &D16(Label v)         { Section::D16(v);      return *this; } | ||||
|   CFISection &D32(uint32_t v)     { Section::D32(v);      return *this; } | ||||
|   CFISection &D32(const Label &v)  { Section::D32(v);      return *this; } | ||||
|   CFISection &D64(uint64_t v)     { Section::D64(v);      return *this; } | ||||
|   CFISection &D64(const Label &v)  { Section::D64(v);      return *this; } | ||||
|   CFISection &LEB128(long long v)  { Section::LEB128(v);   return *this; } | ||||
|   CFISection &ULEB128(uint64_t v) { Section::ULEB128(v);  return *this; } | ||||
|  | ||||
|  private: | ||||
|   // A length value that we've appended to the section, but is not yet | ||||
|   // known. LENGTH is the appended value; START is a label referring | ||||
|   // to the start of the data whose length was cited. | ||||
|   struct PendingLength { | ||||
|     Label length; | ||||
|     Label start; | ||||
|   }; | ||||
|  | ||||
|   // Constants used in CFI/.eh_frame data: | ||||
|  | ||||
|   // If the first four bytes of an "initial length" are this constant, then | ||||
|   // the data uses the 64-bit DWARF format, and the length itself is the | ||||
|   // subsequent eight bytes. | ||||
|   static const uint32_t kDwarf64InitialLengthMarker = 0xffffffffU; | ||||
|  | ||||
|   // The CIE identifier for 32- and 64-bit DWARF CFI and .eh_frame data. | ||||
|   static const uint32_t kDwarf32CIEIdentifier = ~(uint32_t)0; | ||||
|   static const uint64_t kDwarf64CIEIdentifier = ~(uint64_t)0; | ||||
|   static const uint32_t kEHFrame32CIEIdentifier = 0; | ||||
|   static const uint64_t kEHFrame64CIEIdentifier = 0; | ||||
|  | ||||
|   // The size of a machine address for the data in this section. | ||||
|   size_t address_size_; | ||||
|  | ||||
|   // If true, we are generating a Linux .eh_frame section, instead of | ||||
|   // a standard DWARF .debug_frame section. | ||||
|   bool eh_frame_; | ||||
|  | ||||
|   // The encoding to use for FDE pointers. | ||||
|   DwarfPointerEncoding pointer_encoding_; | ||||
|  | ||||
|   // The base addresses to use when emitting encoded pointers. | ||||
|   EncodedPointerBases encoded_pointer_bases_; | ||||
|  | ||||
|   // The length value for the current entry. | ||||
|   // | ||||
|   // Oddly, this must be dynamically allocated. Labels never get new | ||||
|   // values; they only acquire constraints on the value they already | ||||
|   // have, or assert if you assign them something incompatible. So | ||||
|   // each header needs truly fresh Label objects to cite in their | ||||
|   // headers and track their positions. The alternative is explicit | ||||
|   // destructor invocation and a placement new. Ick. | ||||
|   PendingLength *entry_length_; | ||||
|  | ||||
|   // True if we are currently emitting an FDE --- that is, we have | ||||
|   // called FDEHeader but have not yet called FinishEntry. | ||||
|   bool in_fde_; | ||||
|  | ||||
|   // If in_fde_ is true, this is its starting address. We use this for | ||||
|   // emitting DW_EH_PE_funcrel pointers. | ||||
|   uint64_t fde_start_address_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // PROCESSOR_CFI_ASSEMBLER_H_ | ||||
							
								
								
									
										198
									
								
								sdk/breakpad/common/dwarf/dwarf2diehandler.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										198
									
								
								sdk/breakpad/common/dwarf/dwarf2diehandler.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,198 @@ | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class. | ||||
| // See dwarf2diehandler.h for details. | ||||
|  | ||||
| #include <assert.h> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/dwarf/dwarf2diehandler.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| DIEDispatcher::~DIEDispatcher() { | ||||
|   while (!die_handlers_.empty()) { | ||||
|     HandlerStack &entry = die_handlers_.top(); | ||||
|     if (entry.handler_ != root_handler_) | ||||
|       delete entry.handler_; | ||||
|     die_handlers_.pop(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size, | ||||
|                                          uint8 offset_size, uint64 cu_length, | ||||
|                                          uint8 dwarf_version) { | ||||
|   return root_handler_->StartCompilationUnit(offset, address_size, | ||||
|                                              offset_size, cu_length, | ||||
|                                              dwarf_version); | ||||
| } | ||||
|  | ||||
| bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag) { | ||||
|   // The stack entry for the parent of this DIE, if there is one. | ||||
|   HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top(); | ||||
|  | ||||
|   // Does this call indicate that we're done receiving the parent's | ||||
|   // attributes' values?  If so, call its EndAttributes member function. | ||||
|   if (parent && parent->handler_ && !parent->reported_attributes_end_) { | ||||
|     parent->reported_attributes_end_ = true; | ||||
|     if (!parent->handler_->EndAttributes()) { | ||||
|       // Finish off this handler now. and edit *PARENT to indicate that | ||||
|       // we don't want to visit any of the children. | ||||
|       parent->handler_->Finish(); | ||||
|       if (parent->handler_ != root_handler_) | ||||
|         delete parent->handler_; | ||||
|       parent->handler_ = NULL; | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Find a handler for this DIE. | ||||
|   DIEHandler *handler; | ||||
|   if (parent) { | ||||
|     if (parent->handler_) | ||||
|       // Ask the parent to find a handler. | ||||
|       handler = parent->handler_->FindChildHandler(offset, tag); | ||||
|     else | ||||
|       // No parent handler means we're not interested in any of our | ||||
|       // children. | ||||
|       handler = NULL; | ||||
|   } else { | ||||
|     // This is the root DIE.  For a non-root DIE, the parent's handler | ||||
|     // decides whether to visit it, but the root DIE has no parent | ||||
|     // handler, so we have a special method on the root DIE handler | ||||
|     // itself to decide. | ||||
|     if (root_handler_->StartRootDIE(offset, tag)) | ||||
|       handler = root_handler_; | ||||
|     else | ||||
|       handler = NULL; | ||||
|   } | ||||
|  | ||||
|   // Push a handler stack entry for this new handler. As an | ||||
|   // optimization, we don't push NULL-handler entries on top of other | ||||
|   // NULL-handler entries; we just let the oldest such entry stand for | ||||
|   // the whole subtree. | ||||
|   if (handler || !parent || parent->handler_) { | ||||
|     HandlerStack entry; | ||||
|     entry.offset_ = offset; | ||||
|     entry.handler_ = handler; | ||||
|     entry.reported_attributes_end_ = false; | ||||
|     die_handlers_.push(entry); | ||||
|   } | ||||
|  | ||||
|   return handler != NULL; | ||||
| } | ||||
|  | ||||
| void DIEDispatcher::EndDIE(uint64 offset) { | ||||
|   assert(!die_handlers_.empty()); | ||||
|   HandlerStack *entry = &die_handlers_.top(); | ||||
|   if (entry->handler_) { | ||||
|     // This entry had better be the handler for this DIE. | ||||
|     assert(entry->offset_ == offset); | ||||
|     // If a DIE has no children, this EndDIE call indicates that we're | ||||
|     // done receiving its attributes' values. | ||||
|     if (!entry->reported_attributes_end_) | ||||
|       entry->handler_->EndAttributes(); // Ignore return value: no children. | ||||
|     entry->handler_->Finish(); | ||||
|     if (entry->handler_ != root_handler_) | ||||
|       delete entry->handler_; | ||||
|   } else { | ||||
|     // If this DIE is within a tree we're ignoring, then don't pop the | ||||
|     // handler stack: that entry stands for the whole tree. | ||||
|     if (entry->offset_ != offset) | ||||
|       return; | ||||
|   } | ||||
|   die_handlers_.pop(); | ||||
| } | ||||
|  | ||||
| void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset, | ||||
|                                              enum DwarfAttribute attr, | ||||
|                                              enum DwarfForm form, | ||||
|                                              uint64 data) { | ||||
|   HandlerStack ¤t = die_handlers_.top(); | ||||
|   // This had better be an attribute of the DIE we were meant to handle. | ||||
|   assert(offset == current.offset_); | ||||
|   current.handler_->ProcessAttributeUnsigned(attr, form, data); | ||||
| } | ||||
|  | ||||
| void DIEDispatcher::ProcessAttributeSigned(uint64 offset, | ||||
|                                            enum DwarfAttribute attr, | ||||
|                                            enum DwarfForm form, | ||||
|                                            int64 data) { | ||||
|   HandlerStack ¤t = die_handlers_.top(); | ||||
|   // This had better be an attribute of the DIE we were meant to handle. | ||||
|   assert(offset == current.offset_); | ||||
|   current.handler_->ProcessAttributeSigned(attr, form, data); | ||||
| } | ||||
|  | ||||
| void DIEDispatcher::ProcessAttributeReference(uint64 offset, | ||||
|                                               enum DwarfAttribute attr, | ||||
|                                               enum DwarfForm form, | ||||
|                                               uint64 data) { | ||||
|   HandlerStack ¤t = die_handlers_.top(); | ||||
|   // This had better be an attribute of the DIE we were meant to handle. | ||||
|   assert(offset == current.offset_); | ||||
|   current.handler_->ProcessAttributeReference(attr, form, data); | ||||
| } | ||||
|  | ||||
| void DIEDispatcher::ProcessAttributeBuffer(uint64 offset, | ||||
|                                            enum DwarfAttribute attr, | ||||
|                                            enum DwarfForm form, | ||||
|                                            const char* data, | ||||
|                                            uint64 len) { | ||||
|   HandlerStack ¤t = die_handlers_.top(); | ||||
|   // This had better be an attribute of the DIE we were meant to handle. | ||||
|   assert(offset == current.offset_); | ||||
|   current.handler_->ProcessAttributeBuffer(attr, form, data, len); | ||||
| } | ||||
|  | ||||
| void DIEDispatcher::ProcessAttributeString(uint64 offset, | ||||
|                                            enum DwarfAttribute attr, | ||||
|                                            enum DwarfForm form, | ||||
|                                            const string& data) { | ||||
|   HandlerStack ¤t = die_handlers_.top(); | ||||
|   // This had better be an attribute of the DIE we were meant to handle. | ||||
|   assert(offset == current.offset_); | ||||
|   current.handler_->ProcessAttributeString(attr, form, data); | ||||
| } | ||||
|  | ||||
| void DIEDispatcher::ProcessAttributeSignature(uint64 offset, | ||||
|                                               enum DwarfAttribute attr, | ||||
|                                               enum DwarfForm form, | ||||
|                                               uint64 signature) { | ||||
|   HandlerStack ¤t = die_handlers_.top(); | ||||
|   // This had better be an attribute of the DIE we were meant to handle. | ||||
|   assert(offset == current.offset_); | ||||
|   current.handler_->ProcessAttributeSignature(attr, form, signature); | ||||
| } | ||||
|  | ||||
| } // namespace dwarf2reader | ||||
							
								
								
									
										363
									
								
								sdk/breakpad/common/dwarf/dwarf2diehandler.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										363
									
								
								sdk/breakpad/common/dwarf/dwarf2diehandler.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,363 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf2reader::CompilationUnit is a simple and direct parser for | ||||
| // DWARF data, but its handler interface is not convenient to use.  In | ||||
| // particular: | ||||
| // | ||||
| // - CompilationUnit calls Dwarf2Handler's member functions to report | ||||
| //   every attribute's value, regardless of what sort of DIE it is. | ||||
| //   As a result, the ProcessAttributeX functions end up looking like | ||||
| //   this: | ||||
| // | ||||
| //     switch (parent_die_tag) { | ||||
| //       case DW_TAG_x: | ||||
| //         switch (attribute_name) { | ||||
| //           case DW_AT_y: | ||||
| //             handle attribute y of DIE type x | ||||
| //           ... | ||||
| //         } break; | ||||
| //       ... | ||||
| //     }  | ||||
| // | ||||
| //   In C++ it's much nicer to use virtual function dispatch to find | ||||
| //   the right code for a given case than to switch on the DIE tag | ||||
| //   like this. | ||||
| // | ||||
| // - Processing different kinds of DIEs requires different sets of | ||||
| //   data: lexical block DIEs have start and end addresses, but struct | ||||
| //   type DIEs don't.  It would be nice to be able to have separate | ||||
| //   handler classes for separate kinds of DIEs, each with the members | ||||
| //   appropriate to its role, instead of having one handler class that | ||||
| //   needs to hold data for every DIE type. | ||||
| // | ||||
| // - There should be a separate instance of the appropriate handler | ||||
| //   class for each DIE, instead of a single object with tables | ||||
| //   tracking all the dies in the compilation unit. | ||||
| // | ||||
| // - It's not convenient to take some action after all a DIE's | ||||
| //   attributes have been seen, but before visiting any of its | ||||
| //   children.  The only indication you have that a DIE's attribute | ||||
| //   list is complete is that you get either a StartDIE or an EndDIE | ||||
| //   call. | ||||
| // | ||||
| // - It's not convenient to make use of the tree structure of the | ||||
| //   DIEs.  Skipping all the children of a given die requires | ||||
| //   maintaining state and returning false from StartDIE until we get | ||||
| //   an EndDIE call with the appropriate offset. | ||||
| // | ||||
| // This interface tries to take care of all that.  (You're shocked, I'm sure.) | ||||
| // | ||||
| // Using the classes here, you provide an initial handler for the root | ||||
| // DIE of the compilation unit.  Each handler receives its DIE's | ||||
| // attributes, and provides fresh handler objects for children of | ||||
| // interest, if any.  The three classes are: | ||||
| // | ||||
| // - DIEHandler: the base class for your DIE-type-specific handler | ||||
| //   classes. | ||||
| // | ||||
| // - RootDIEHandler: derived from DIEHandler, the base class for your | ||||
| //   root DIE handler class. | ||||
| // | ||||
| // - DIEDispatcher: derived from Dwarf2Handler, an instance of this | ||||
| //   invokes your DIE-type-specific handler objects. | ||||
| // | ||||
| // In detail: | ||||
| // | ||||
| // - Define handler classes specialized for the DIE types you're | ||||
| //   interested in.  These handler classes must inherit from | ||||
| //   DIEHandler.  Thus: | ||||
| // | ||||
| //     class My_DW_TAG_X_Handler: public DIEHandler { ... }; | ||||
| //     class My_DW_TAG_Y_Handler: public DIEHandler { ... }; | ||||
| // | ||||
| //   DIEHandler subclasses needn't correspond exactly to single DIE | ||||
| //   types, as shown here; the point is that you can have several | ||||
| //   different classes appropriate to different kinds of DIEs. | ||||
| // | ||||
| // - In particular, define a handler class for the compilation | ||||
| //   unit's root DIE, that inherits from RootDIEHandler: | ||||
| // | ||||
| //     class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... }; | ||||
| // | ||||
| //   RootDIEHandler inherits from DIEHandler, adding a few additional | ||||
| //   member functions for examining the compilation unit as a whole, | ||||
| //   and other quirks of rootness. | ||||
| // | ||||
| // - Then, create a DIEDispatcher instance, passing it an instance of | ||||
| //   your root DIE handler class, and use that DIEDispatcher as the | ||||
| //   dwarf2reader::CompilationUnit's handler: | ||||
| // | ||||
| //     My_DW_TAG_compile_unit_Handler root_die_handler(...); | ||||
| //     DIEDispatcher die_dispatcher(&root_die_handler); | ||||
| //     CompilationUnit reader(sections, offset, bytereader, &die_dispatcher); | ||||
| // | ||||
| //   Here, 'die_dispatcher' acts as a shim between 'reader' and the | ||||
| //   various DIE-specific handlers you have defined. | ||||
| // | ||||
| // - When you call reader.Start(), die_dispatcher behaves as follows, | ||||
| //   starting with your root die handler and the compilation unit's | ||||
| //   root DIE: | ||||
| // | ||||
| //   - It calls the handler's ProcessAttributeX member functions for | ||||
| //     each of the DIE's attributes. | ||||
| // | ||||
| //   - It calls the handler's EndAttributes member function.  This | ||||
| //     should return true if any of the DIE's children should be | ||||
| //     visited, in which case: | ||||
| // | ||||
| //     - For each of the DIE's children, die_dispatcher calls the | ||||
| //       DIE's handler's FindChildHandler member function.  If that | ||||
| //       returns a pointer to a DIEHandler instance, then | ||||
| //       die_dispatcher uses that handler to process the child, using | ||||
| //       this procedure recursively.  Alternatively, if | ||||
| //       FindChildHandler returns NULL, die_dispatcher ignores that | ||||
| //       child and its descendants. | ||||
| //  | ||||
| //   - When die_dispatcher has finished processing all the DIE's | ||||
| //     children, it invokes the handler's Finish() member function, | ||||
| //     and destroys the handler.  (As a special case, it doesn't | ||||
| //     destroy the root DIE handler.) | ||||
| //  | ||||
| // This allows the code for handling a particular kind of DIE to be | ||||
| // gathered together in a single class, makes it easy to skip all the | ||||
| // children or individual children of a particular DIE, and provides | ||||
| // appropriate parental context for each die. | ||||
|  | ||||
| #ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__ | ||||
| #define COMMON_DWARF_DWARF2DIEHANDLER_H__ | ||||
|  | ||||
| #include <stack> | ||||
| #include <string> | ||||
|  | ||||
| #include "common/dwarf/types.h" | ||||
| #include "common/dwarf/dwarf2enums.h" | ||||
| #include "common/dwarf/dwarf2reader.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| // A base class for handlers for specific DIE types.  The series of | ||||
| // calls made on a DIE handler is as follows: | ||||
| // | ||||
| // - for each attribute of the DIE: | ||||
| //   - ProcessAttributeX() | ||||
| // - EndAttributes() | ||||
| // - if that returned true, then for each child: | ||||
| //   - FindChildHandler() | ||||
| //   - if that returns a non-NULL pointer to a new handler: | ||||
| //     - recurse, with the new handler and the child die | ||||
| // - Finish() | ||||
| // - destruction | ||||
| class DIEHandler { | ||||
|  public: | ||||
|   DIEHandler() { } | ||||
|   virtual ~DIEHandler() { } | ||||
|  | ||||
|   // When we visit a DIE, we first use these member functions to | ||||
|   // report the DIE's attributes and their values.  These have the | ||||
|   // same restrictions as the corresponding member functions of | ||||
|   // dwarf2reader::Dwarf2Handler. | ||||
|   // | ||||
|   // Since DWARF does not specify in what order attributes must | ||||
|   // appear, avoid making decisions in these functions that would be | ||||
|   // affected by the presence of other attributes. The EndAttributes | ||||
|   // function is a more appropriate place for such work, as all the | ||||
|   // DIE's attributes have been seen at that point. | ||||
|   // | ||||
|   // The default definitions ignore the values they are passed. | ||||
|   virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr, | ||||
|                                         enum DwarfForm form, | ||||
|                                         uint64 data) { } | ||||
|   virtual void ProcessAttributeSigned(enum DwarfAttribute attr, | ||||
|                                       enum DwarfForm form, | ||||
|                                       int64 data) { } | ||||
|   virtual void ProcessAttributeReference(enum DwarfAttribute attr, | ||||
|                                          enum DwarfForm form, | ||||
|                                          uint64 data) { } | ||||
|   virtual void ProcessAttributeBuffer(enum DwarfAttribute attr, | ||||
|                                       enum DwarfForm form, | ||||
|                                       const char* data, | ||||
|                                       uint64 len) { } | ||||
|   virtual void ProcessAttributeString(enum DwarfAttribute attr, | ||||
|                                       enum DwarfForm form, | ||||
|                                       const string& data) { } | ||||
|   virtual void ProcessAttributeSignature(enum DwarfAttribute attr, | ||||
|                                          enum DwarfForm form, | ||||
|                                          uint64 signture) { } | ||||
|  | ||||
|   // Once we have reported all the DIE's attributes' values, we call | ||||
|   // this member function.  If it returns false, we skip all the DIE's | ||||
|   // children.  If it returns true, we call FindChildHandler on each | ||||
|   // child.  If that returns a handler object, we use that to visit | ||||
|   // the child; otherwise, we skip the child. | ||||
|   // | ||||
|   // This is a good place to make decisions that depend on more than | ||||
|   // one attribute. DWARF does not specify in what order attributes | ||||
|   // must appear, so only when the EndAttributes function is called | ||||
|   // does the handler have a complete picture of the DIE's attributes. | ||||
|   // | ||||
|   // The default definition elects to ignore the DIE's children. | ||||
|   // You'll need to override this if you override FindChildHandler, | ||||
|   // but at least the default behavior isn't to pass the children to | ||||
|   // FindChildHandler, which then ignores them all. | ||||
|   virtual bool EndAttributes() { return false; } | ||||
|  | ||||
|   // If EndAttributes returns true to indicate that some of the DIE's | ||||
|   // children might be of interest, then we apply this function to | ||||
|   // each of the DIE's children.  If it returns a handler object, then | ||||
|   // we use that to visit the child DIE.  If it returns NULL, we skip | ||||
|   // that child DIE (and all its descendants). | ||||
|   // | ||||
|   // OFFSET is the offset of the child; TAG indicates what kind of DIE | ||||
|   // it is. | ||||
|   // | ||||
|   // The default definition skips all children. | ||||
|   virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag) { | ||||
|     return NULL; | ||||
|   } | ||||
|  | ||||
|   // When we are done processing a DIE, we call this member function. | ||||
|   // This happens after the EndAttributes call, all FindChildHandler | ||||
|   // calls (if any), and all operations on the children themselves (if | ||||
|   // any). We call Finish on every handler --- even if EndAttributes | ||||
|   // returns false. | ||||
|   virtual void Finish() { }; | ||||
| }; | ||||
|  | ||||
| // A subclass of DIEHandler, with additional kludges for handling the | ||||
| // compilation unit's root die. | ||||
| class RootDIEHandler: public DIEHandler { | ||||
|  public: | ||||
|   RootDIEHandler() { } | ||||
|   virtual ~RootDIEHandler() { } | ||||
|  | ||||
|   // We pass the values reported via Dwarf2Handler::StartCompilationUnit | ||||
|   // to this member function, and skip the entire compilation unit if it | ||||
|   // returns false.  So the root DIE handler is actually also | ||||
|   // responsible for handling the compilation unit metadata. | ||||
|   // The default definition always visits the compilation unit. | ||||
|   virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||
|                                     uint8 offset_size, uint64 cu_length, | ||||
|                                     uint8 dwarf_version) { return true; } | ||||
|  | ||||
|   // For the root DIE handler only, we pass the offset, tag and | ||||
|   // attributes of the compilation unit's root DIE.  This is the only | ||||
|   // way the root DIE handler can find the root DIE's tag.  If this | ||||
|   // function returns true, we will visit the root DIE using the usual | ||||
|   // DIEHandler methods; otherwise, we skip the entire compilation | ||||
|   // unit. | ||||
|   // | ||||
|   // The default definition elects to visit the root DIE. | ||||
|   virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag) { return true; } | ||||
| }; | ||||
|  | ||||
| class DIEDispatcher: public Dwarf2Handler { | ||||
|  public: | ||||
|   // Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for | ||||
|   // the compilation unit's root die, as described for the DIEHandler | ||||
|   // class. | ||||
|   DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { } | ||||
|   // Destroying a DIEDispatcher destroys all active handler objects | ||||
|   // except the root handler. | ||||
|   ~DIEDispatcher(); | ||||
|   bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||
|                             uint8 offset_size, uint64 cu_length, | ||||
|                             uint8 dwarf_version); | ||||
|   bool StartDIE(uint64 offset, enum DwarfTag tag); | ||||
|   void ProcessAttributeUnsigned(uint64 offset, | ||||
|                                 enum DwarfAttribute attr, | ||||
|                                 enum DwarfForm form, | ||||
|                                 uint64 data); | ||||
|   void ProcessAttributeSigned(uint64 offset, | ||||
|                               enum DwarfAttribute attr, | ||||
|                               enum DwarfForm form, | ||||
|                               int64 data); | ||||
|   void ProcessAttributeReference(uint64 offset, | ||||
|                                  enum DwarfAttribute attr, | ||||
|                                  enum DwarfForm form, | ||||
|                                  uint64 data); | ||||
|   void ProcessAttributeBuffer(uint64 offset, | ||||
|                               enum DwarfAttribute attr, | ||||
|                               enum DwarfForm form, | ||||
|                               const char* data, | ||||
|                               uint64 len); | ||||
|   void ProcessAttributeString(uint64 offset, | ||||
|                               enum DwarfAttribute attr, | ||||
|                               enum DwarfForm form, | ||||
|                               const string &data); | ||||
|   void ProcessAttributeSignature(uint64 offset, | ||||
|                                  enum DwarfAttribute attr, | ||||
|                                  enum DwarfForm form, | ||||
|                                  uint64 signature); | ||||
|   void EndDIE(uint64 offset); | ||||
|  | ||||
|  private: | ||||
|  | ||||
|   // The type of a handler stack entry.  This includes some fields | ||||
|   // which don't really need to be on the stack --- they could just be | ||||
|   // single data members of DIEDispatcher --- but putting them here | ||||
|   // makes it easier to see that the code is correct. | ||||
|   struct HandlerStack { | ||||
|     // The offset of the DIE for this handler stack entry. | ||||
|     uint64 offset_; | ||||
|  | ||||
|     // The handler object interested in this DIE's attributes and | ||||
|     // children.  If NULL, we're not interested in either. | ||||
|     DIEHandler *handler_; | ||||
|  | ||||
|     // Have we reported the end of this DIE's attributes to the handler? | ||||
|     bool reported_attributes_end_; | ||||
|   }; | ||||
|  | ||||
|   // Stack of DIE attribute handlers.  At StartDIE(D), the top of the | ||||
|   // stack is the handler of D's parent, whom we may ask for a handler | ||||
|   // for D itself.  At EndDIE(D), the top of the stack is D's handler. | ||||
|   // Special cases: | ||||
|   // | ||||
|   // - Before we've seen the compilation unit's root DIE, the stack is | ||||
|   //   empty; we'll call root_handler_'s special member functions, and | ||||
|   //   perhaps push root_handler_ on the stack to look at the root's | ||||
|   //   immediate children. | ||||
|   // | ||||
|   // - When we decide to ignore a subtree, we only push an entry on | ||||
|   //   the stack for the root of the tree being ignored, rather than | ||||
|   //   pushing lots of stack entries with handler_ set to NULL. | ||||
|   std::stack<HandlerStack> die_handlers_; | ||||
|  | ||||
|   // The root handler.  We don't push it on die_handlers_ until we | ||||
|   // actually get the StartDIE call for the root. | ||||
|   RootDIEHandler *root_handler_; | ||||
| }; | ||||
|  | ||||
| } // namespace dwarf2reader | ||||
| #endif  // COMMON_DWARF_DWARF2DIEHANDLER_H__ | ||||
							
								
								
									
										524
									
								
								sdk/breakpad/common/dwarf/dwarf2diehandler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										524
									
								
								sdk/breakpad/common/dwarf/dwarf2diehandler_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,524 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher. | ||||
|  | ||||
| #include <string> | ||||
| #include <utility> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
|  | ||||
| #include "common/dwarf/dwarf2diehandler.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using std::make_pair; | ||||
|  | ||||
| using ::testing::_; | ||||
| using ::testing::ContainerEq; | ||||
| using ::testing::ElementsAreArray; | ||||
| using ::testing::Eq; | ||||
| using ::testing::InSequence; | ||||
| using ::testing::Return; | ||||
| using ::testing::Sequence; | ||||
| using ::testing::StrEq; | ||||
|  | ||||
| using dwarf2reader::DIEDispatcher; | ||||
| using dwarf2reader::DIEHandler; | ||||
| using dwarf2reader::DwarfAttribute; | ||||
| using dwarf2reader::DwarfForm; | ||||
| using dwarf2reader::DwarfTag; | ||||
| using dwarf2reader::RootDIEHandler; | ||||
|  | ||||
| class MockDIEHandler: public DIEHandler { | ||||
|  public: | ||||
|   MOCK_METHOD3(ProcessAttributeUnsigned, | ||||
|                void(DwarfAttribute, DwarfForm, uint64)); | ||||
|   MOCK_METHOD3(ProcessAttributeSigned, | ||||
|                void(DwarfAttribute, DwarfForm, int64)); | ||||
|   MOCK_METHOD3(ProcessAttributeReference, | ||||
|                void(DwarfAttribute, DwarfForm, uint64)); | ||||
|   MOCK_METHOD4(ProcessAttributeBuffer, | ||||
|                void(DwarfAttribute, DwarfForm, const char *, uint64)); | ||||
|   MOCK_METHOD3(ProcessAttributeString, | ||||
|                void(DwarfAttribute, DwarfForm, const string &)); | ||||
|   MOCK_METHOD3(ProcessAttributeSignature, | ||||
|                void(DwarfAttribute, DwarfForm, uint64)); | ||||
|   MOCK_METHOD0(EndAttributes, bool()); | ||||
|   MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); | ||||
|   MOCK_METHOD0(Finish, void()); | ||||
| }; | ||||
|  | ||||
| class MockRootDIEHandler: public RootDIEHandler { | ||||
|  public: | ||||
|   MOCK_METHOD3(ProcessAttributeUnsigned, | ||||
|                void(DwarfAttribute, DwarfForm, uint64)); | ||||
|   MOCK_METHOD3(ProcessAttributeSigned, | ||||
|                void(DwarfAttribute, DwarfForm, int64)); | ||||
|   MOCK_METHOD3(ProcessAttributeReference, | ||||
|                void(DwarfAttribute, DwarfForm, uint64)); | ||||
|   MOCK_METHOD4(ProcessAttributeBuffer, | ||||
|                void(DwarfAttribute, DwarfForm, const char *, uint64)); | ||||
|   MOCK_METHOD3(ProcessAttributeString, | ||||
|                void(DwarfAttribute, DwarfForm, const string &)); | ||||
|   MOCK_METHOD3(ProcessAttributeSignature, | ||||
|                void(DwarfAttribute, DwarfForm, uint64)); | ||||
|   MOCK_METHOD0(EndAttributes, bool()); | ||||
|   MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); | ||||
|   MOCK_METHOD0(Finish, void()); | ||||
|   MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8)); | ||||
|   MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag)); | ||||
| }; | ||||
|  | ||||
| // If the handler elects to skip the compilation unit, the dispatcher | ||||
| // should tell the reader so. | ||||
| TEST(Dwarf2DIEHandler, SkipCompilationUnit) { | ||||
|   Sequence s; | ||||
|   MockRootDIEHandler mock_root_handler; | ||||
|   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||
|  | ||||
|   EXPECT_CALL(mock_root_handler, | ||||
|               StartCompilationUnit(0x8d42aed77cfccf3eLL, | ||||
|                                    0x89, 0xdc, | ||||
|                                    0x2ecb4dc778a80f21LL, | ||||
|                                    0x66)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return(false)); | ||||
|  | ||||
|   EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, | ||||
|                                                    0x89, 0xdc, | ||||
|                                                    0x2ecb4dc778a80f21LL, | ||||
|                                                    0x66)); | ||||
| } | ||||
|  | ||||
| // If the handler elects to skip the root DIE, the dispatcher should | ||||
| // tell the reader so. | ||||
| TEST(Dwarf2DIEHandler, SkipRootDIE) { | ||||
|   Sequence s; | ||||
|   MockRootDIEHandler mock_root_handler; | ||||
|   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||
|  | ||||
|   EXPECT_CALL(mock_root_handler, | ||||
|               StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02, | ||||
|                                    0xb00febffa76e2b2bLL, 0x5c)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return(true)); | ||||
|   EXPECT_CALL(mock_root_handler, | ||||
|               StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return(false)); | ||||
|  | ||||
|   EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL,  | ||||
|                                                   0xf4, 0x02, | ||||
|                                                   0xb00febffa76e2b2bLL, 0x5c)); | ||||
|   EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, | ||||
|                                        (DwarfTag) 0xb4f98da6)); | ||||
|   die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); | ||||
| } | ||||
|  | ||||
| // If the handler elects to skip the root DIE's children, the | ||||
| // dispatcher should tell the reader so --- and avoid deleting the | ||||
| // root handler. | ||||
| TEST(Dwarf2DIEHandler, SkipRootDIEChildren) { | ||||
|   MockRootDIEHandler mock_root_handler; | ||||
|   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||
|  | ||||
|   { | ||||
|     InSequence s; | ||||
|  | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0, | ||||
|                                      0x09f8bf0767f91675LL, 0xdb)) | ||||
|       .WillOnce(Return(true)); | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) | ||||
|       .WillOnce(Return(true)); | ||||
|     // Please don't tell me about my children. | ||||
|     EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||
|       .WillOnce(Return(false)); | ||||
|     EXPECT_CALL(mock_root_handler, Finish()) | ||||
|       .WillOnce(Return()); | ||||
|   } | ||||
|  | ||||
|   EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL, | ||||
|                                                   0x26, 0xa0, | ||||
|                                                   0x09f8bf0767f91675LL, 0xdb)); | ||||
|   EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, | ||||
|                                       (DwarfTag) 0xb4f98da6)); | ||||
|   EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL, | ||||
|                                        (DwarfTag) 0xc3a17bba)); | ||||
|   die_dispatcher.EndDIE(0x435150ceedccda18LL); | ||||
|   die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); | ||||
| } | ||||
|  | ||||
| // The dispatcher should pass attribute values through to the die | ||||
| // handler accurately. | ||||
| TEST(Dwarf2DIEHandler, PassAttributeValues) { | ||||
|   MockRootDIEHandler mock_root_handler; | ||||
|   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||
|  | ||||
|   const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca, | ||||
|                             0xcf, 0xa8, 0x84, 0xa7, 0x18 }; | ||||
|   string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d"; | ||||
|  | ||||
|   // Set expectations. | ||||
|   { | ||||
|     InSequence s; | ||||
|  | ||||
|     // We'll like the compilation unit header. | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc, | ||||
|                                      0x2ecb4dc778a80f21LL, 0x66)) | ||||
|       .WillOnce(Return(true)); | ||||
|  | ||||
|     // We'll like the root DIE. | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c)) | ||||
|       .WillOnce(Return(true)); | ||||
|  | ||||
|     // Expect some attribute values. | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed, | ||||
|                                          (DwarfForm) 0x424f1468, | ||||
|                                          0xa592571997facda1ULL)) | ||||
|       .WillOnce(Return()); | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 ProcessAttributeSigned((DwarfAttribute) 0x43694dc9, | ||||
|                                        (DwarfForm) 0xf6f78901L, | ||||
|                                        0x92602a4e3bf1f446LL)) | ||||
|       .WillOnce(Return()); | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 ProcessAttributeReference((DwarfAttribute) 0x4033e8cL, | ||||
|                                           (DwarfForm) 0xf66fbe0bL, | ||||
|                                           0x50fddef44734fdecULL)) | ||||
|       .WillOnce(Return()); | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af, | ||||
|                                        (DwarfForm) 0xe99a539a, | ||||
|                                        buffer, sizeof(buffer))) | ||||
|       .WillOnce(Return()); | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 ProcessAttributeString((DwarfAttribute) 0x310ed065, | ||||
|                                        (DwarfForm) 0x15762fec, | ||||
|                                        StrEq(str))) | ||||
|       .WillOnce(Return()); | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 ProcessAttributeSignature((DwarfAttribute) 0x58790d72, | ||||
|                                           (DwarfForm) 0x4159f138, | ||||
|                                           0x94682463613e6a5fULL)) | ||||
|       .WillOnce(Return()); | ||||
|     EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||
|       .WillOnce(Return(true)); | ||||
|     EXPECT_CALL(mock_root_handler, FindChildHandler(_, _)) | ||||
|       .Times(0); | ||||
|     EXPECT_CALL(mock_root_handler, Finish()) | ||||
|       .WillOnce(Return()); | ||||
|   } | ||||
|  | ||||
|   // Drive the dispatcher. | ||||
|  | ||||
|   // Report the CU header. | ||||
|   EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, | ||||
|                                                   0x89, 0xdc, | ||||
|                                                   0x2ecb4dc778a80f21LL, | ||||
|                                                   0x66)); | ||||
|   // Report the root DIE. | ||||
|   EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL, | ||||
|                                       (DwarfTag) 0x9829445c)); | ||||
|  | ||||
|   // Report some attribute values. | ||||
|   die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL, | ||||
|                                           (DwarfAttribute) 0x1cc0bfed, | ||||
|                                           (DwarfForm) 0x424f1468, | ||||
|                                           0xa592571997facda1ULL); | ||||
|   die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL, | ||||
|                                         (DwarfAttribute) 0x43694dc9, | ||||
|                                         (DwarfForm) 0xf6f78901, | ||||
|                                         0x92602a4e3bf1f446LL); | ||||
|   die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL, | ||||
|                                            (DwarfAttribute) 0x4033e8c, | ||||
|                                            (DwarfForm) 0xf66fbe0b, | ||||
|                                            0x50fddef44734fdecULL); | ||||
|   die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL, | ||||
|                                         (DwarfAttribute) 0x25d7e0af, | ||||
|                                         (DwarfForm) 0xe99a539a, | ||||
|                                         buffer, sizeof(buffer)); | ||||
|   die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL, | ||||
|                                         (DwarfAttribute) 0x310ed065, | ||||
|                                         (DwarfForm) 0x15762fec, | ||||
|                                         str); | ||||
|   die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL, | ||||
|                                            (DwarfAttribute) 0x58790d72, | ||||
|                                            (DwarfForm) 0x4159f138, | ||||
|                                            0x94682463613e6a5fULL); | ||||
|  | ||||
|   // Finish the root DIE (and thus the CU). | ||||
|   die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); | ||||
| } | ||||
|  | ||||
| TEST(Dwarf2DIEHandler, FindAndSkipChildren) { | ||||
|   MockRootDIEHandler mock_root_handler; | ||||
|   MockDIEHandler *mock_child1_handler = new(MockDIEHandler); | ||||
|   MockDIEHandler *mock_child3_handler = new(MockDIEHandler); | ||||
|   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||
|  | ||||
|   { | ||||
|     InSequence s; | ||||
|  | ||||
|     // We'll like the compilation unit header. | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, | ||||
|                                      0x47dd3c764275a216LL, 0xa5)) | ||||
|       .WillOnce(Return(true)); | ||||
|  | ||||
|     // Root DIE. | ||||
|     { | ||||
|       EXPECT_CALL(mock_root_handler, | ||||
|                   StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59)) | ||||
|         .WillOnce(Return(true)); | ||||
|       EXPECT_CALL(mock_root_handler, | ||||
|                   ProcessAttributeSigned((DwarfAttribute) 0xf779a642, | ||||
|                                          (DwarfForm) 0x2cb63027, | ||||
|                                          0x18e744661769d08fLL)) | ||||
|         .WillOnce(Return()); | ||||
|       EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||
|         .WillOnce(Return(true)); | ||||
|  | ||||
|       // First child DIE. | ||||
|       EXPECT_CALL(mock_root_handler, | ||||
|                   FindChildHandler(0x149f644f8116fe8cLL, | ||||
|                                    (DwarfTag) 0xac2cbd8c)) | ||||
|         .WillOnce(Return(mock_child1_handler)); | ||||
|       { | ||||
|         EXPECT_CALL(*mock_child1_handler, | ||||
|                     ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65, | ||||
|                                            (DwarfForm) 0xe4f64c41, | ||||
|                                            0x1b04e5444a55fe67LL)) | ||||
|           .WillOnce(Return()); | ||||
|         EXPECT_CALL(*mock_child1_handler, EndAttributes()) | ||||
|           .WillOnce(Return(false)); | ||||
|         // Skip first grandchild DIE and first great-grandchild DIE. | ||||
|         EXPECT_CALL(*mock_child1_handler, Finish()) | ||||
|           .WillOnce(Return()); | ||||
|       } | ||||
|  | ||||
|       // Second child DIE.  Root handler will decline to return a handler | ||||
|       // for this child. | ||||
|       EXPECT_CALL(mock_root_handler, | ||||
|                   FindChildHandler(0x97412be24875de9dLL, | ||||
|                                    (DwarfTag) 0x505a068b)) | ||||
|         .WillOnce(Return((DIEHandler *) NULL)); | ||||
|  | ||||
|       // Third child DIE. | ||||
|       EXPECT_CALL(mock_root_handler, | ||||
|                   FindChildHandler(0x753c964c8ab538aeLL, | ||||
|                                    (DwarfTag) 0x8c22970e)) | ||||
|         .WillOnce(Return(mock_child3_handler)); | ||||
|       { | ||||
|         EXPECT_CALL(*mock_child3_handler, | ||||
|                     ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, | ||||
|                                            (DwarfForm) 0x610b7ae1, | ||||
|                                            0x3ea5c609d7d7560fLL)) | ||||
|           .WillOnce(Return()); | ||||
|         EXPECT_CALL(*mock_child3_handler, EndAttributes()) | ||||
|           .WillOnce(Return(true)); | ||||
|         EXPECT_CALL(*mock_child3_handler, Finish()) | ||||
|           .WillOnce(Return()); | ||||
|       } | ||||
|  | ||||
|       EXPECT_CALL(mock_root_handler, Finish()) | ||||
|         .WillOnce(Return()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|      | ||||
|   // Drive the dispatcher. | ||||
|  | ||||
|   // Report the CU header. | ||||
|   EXPECT_TRUE(die_dispatcher | ||||
|               .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, | ||||
|                                     0x47dd3c764275a216LL, 0xa5)); | ||||
|   // Report the root DIE. | ||||
|   { | ||||
|     EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL, | ||||
|                                         (DwarfTag) 0xf5d60c59)); | ||||
|     die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL, | ||||
|                                           (DwarfAttribute) 0xf779a642, | ||||
|                                           (DwarfForm) 0x2cb63027, | ||||
|                                           0x18e744661769d08fLL); | ||||
|  | ||||
|     // First child DIE. | ||||
|     { | ||||
|       EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL, | ||||
|                                           (DwarfTag) 0xac2cbd8c)); | ||||
|       die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL, | ||||
|                                             (DwarfAttribute) 0xa6fd6f65, | ||||
|                                             (DwarfForm) 0xe4f64c41, | ||||
|                                             0x1b04e5444a55fe67LL); | ||||
|  | ||||
|       // First grandchild DIE.  Will be skipped. | ||||
|       { | ||||
|         EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL, | ||||
|                                             (DwarfTag) 0x22f05a15)); | ||||
|         // First great-grandchild DIE.  Will be skipped without being | ||||
|         // mentioned to any handler. | ||||
|         { | ||||
|           EXPECT_FALSE(die_dispatcher | ||||
|                        .StartDIE(0xb3076285d25cac25LL, | ||||
|                                  (DwarfTag) 0xcff4061b)); | ||||
|           die_dispatcher.EndDIE(0xb3076285d25cac25LL);           | ||||
|         } | ||||
|         die_dispatcher.EndDIE(0xd68de1ee0bd29419LL); | ||||
|       } | ||||
|       die_dispatcher.EndDIE(0x149f644f8116fe8cLL); | ||||
|     } | ||||
|  | ||||
|     // Second child DIE.  Root handler will decline to find a handler for it. | ||||
|     { | ||||
|       EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL, | ||||
|                                            (DwarfTag) 0x505a068b)); | ||||
|       die_dispatcher.EndDIE(0x97412be24875de9dLL); | ||||
|     } | ||||
|      | ||||
|     // Third child DIE. | ||||
|     { | ||||
|       EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL, | ||||
|                                           (DwarfTag) 0x8c22970e)); | ||||
|       die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL, | ||||
|                                             (DwarfAttribute) 0x4e2b7cfb, | ||||
|                                             (DwarfForm) 0x610b7ae1, | ||||
|                                             0x3ea5c609d7d7560fLL); | ||||
|       die_dispatcher.EndDIE(0x753c964c8ab538aeLL); | ||||
|     } | ||||
|      | ||||
|     // Finish the root DIE (and thus the CU). | ||||
|     die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // The DIEDispatcher destructor is supposed to delete all handlers on | ||||
| // the stack, except for the root. | ||||
| TEST(Dwarf2DIEHandler, FreeHandlersOnStack) { | ||||
|   MockRootDIEHandler mock_root_handler; | ||||
|   MockDIEHandler *mock_child_handler = new(MockDIEHandler); | ||||
|   MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler); | ||||
|  | ||||
|   { | ||||
|     InSequence s; | ||||
|  | ||||
|     // We'll like the compilation unit header. | ||||
|     EXPECT_CALL(mock_root_handler, | ||||
|                 StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, | ||||
|                                      0x76d392ff393ddda2LL, 0xbf)) | ||||
|       .WillOnce(Return(true)); | ||||
|  | ||||
|     // Root DIE. | ||||
|     { | ||||
|       EXPECT_CALL(mock_root_handler, | ||||
|                   StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361)) | ||||
|         .WillOnce(Return(true)); | ||||
|       EXPECT_CALL(mock_root_handler, EndAttributes()) | ||||
|         .WillOnce(Return(true)); | ||||
|        | ||||
|       // Child DIE. | ||||
|       EXPECT_CALL(mock_root_handler, | ||||
|                   FindChildHandler(0x058f09240c5fc8c9LL, | ||||
|                                    (DwarfTag) 0x898bf0d0)) | ||||
|         .WillOnce(Return(mock_child_handler)); | ||||
|       { | ||||
|         EXPECT_CALL(*mock_child_handler, EndAttributes()) | ||||
|           .WillOnce(Return(true)); | ||||
|  | ||||
|         // Grandchild DIE. | ||||
|         EXPECT_CALL(*mock_child_handler, | ||||
|                     FindChildHandler(0x32dc00c9945dc0c8LL, | ||||
|                                      (DwarfTag) 0x2802d007)) | ||||
|           .WillOnce(Return(mock_grandchild_handler)); | ||||
|         { | ||||
|           EXPECT_CALL(*mock_grandchild_handler, | ||||
|                       ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, | ||||
|                                              (DwarfForm) 0x610b7ae1, | ||||
|                                              0x3ea5c609d7d7560fLL)) | ||||
|             .WillOnce(Return()); | ||||
|  | ||||
|           // At this point, we abandon the traversal, so none of the | ||||
|           // usual stuff should get called. | ||||
|           EXPECT_CALL(*mock_grandchild_handler, EndAttributes()) | ||||
|             .Times(0); | ||||
|           EXPECT_CALL(*mock_grandchild_handler, Finish()) | ||||
|             .Times(0); | ||||
|         } | ||||
|  | ||||
|         EXPECT_CALL(*mock_child_handler, Finish()) | ||||
|           .Times(0); | ||||
|       } | ||||
|  | ||||
|       EXPECT_CALL(mock_root_handler, Finish()) | ||||
|         .Times(0); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // The dispatcher. | ||||
|   DIEDispatcher die_dispatcher(&mock_root_handler); | ||||
|    | ||||
|   // Report the CU header. | ||||
|   EXPECT_TRUE(die_dispatcher | ||||
|               .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, | ||||
|                                     0x76d392ff393ddda2LL, 0xbf)); | ||||
|   // Report the root DIE. | ||||
|   { | ||||
|     EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL, | ||||
|                                         (DwarfTag) 0x98980361)); | ||||
|  | ||||
|     // Child DIE. | ||||
|     { | ||||
|       EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL, | ||||
|                                           (DwarfTag) 0x898bf0d0)); | ||||
|  | ||||
|       // Grandchild DIE. | ||||
|       { | ||||
|         EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL, | ||||
|                                             (DwarfTag) 0x2802d007)); | ||||
|         die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL, | ||||
|                                               (DwarfAttribute) 0x4e2b7cfb, | ||||
|                                               (DwarfForm) 0x610b7ae1, | ||||
|                                               0x3ea5c609d7d7560fLL); | ||||
|  | ||||
|         // Stop the traversal abruptly, so that there will still be | ||||
|         // handlers on the stack when the dispatcher is destructed. | ||||
|  | ||||
|         // No EndDIE call... | ||||
|       } | ||||
|       // No EndDIE call... | ||||
|     } | ||||
|     // No EndDIE call... | ||||
|   } | ||||
| } | ||||
							
								
								
									
										650
									
								
								sdk/breakpad/common/dwarf/dwarf2enums.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										650
									
								
								sdk/breakpad/common/dwarf/dwarf2enums.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,650 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef COMMON_DWARF_DWARF2ENUMS_H__ | ||||
| #define COMMON_DWARF_DWARF2ENUMS_H__ | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| // These enums do not follow the google3 style only because they are | ||||
| // known universally (specs, other implementations) by the names in | ||||
| // exactly this capitalization. | ||||
| // Tag names and codes. | ||||
| enum DwarfTag { | ||||
|   DW_TAG_padding = 0x00, | ||||
|   DW_TAG_array_type = 0x01, | ||||
|   DW_TAG_class_type = 0x02, | ||||
|   DW_TAG_entry_point = 0x03, | ||||
|   DW_TAG_enumeration_type = 0x04, | ||||
|   DW_TAG_formal_parameter = 0x05, | ||||
|   DW_TAG_imported_declaration = 0x08, | ||||
|   DW_TAG_label = 0x0a, | ||||
|   DW_TAG_lexical_block = 0x0b, | ||||
|   DW_TAG_member = 0x0d, | ||||
|   DW_TAG_pointer_type = 0x0f, | ||||
|   DW_TAG_reference_type = 0x10, | ||||
|   DW_TAG_compile_unit = 0x11, | ||||
|   DW_TAG_string_type = 0x12, | ||||
|   DW_TAG_structure_type = 0x13, | ||||
|   DW_TAG_subroutine_type = 0x15, | ||||
|   DW_TAG_typedef = 0x16, | ||||
|   DW_TAG_union_type = 0x17, | ||||
|   DW_TAG_unspecified_parameters = 0x18, | ||||
|   DW_TAG_variant = 0x19, | ||||
|   DW_TAG_common_block = 0x1a, | ||||
|   DW_TAG_common_inclusion = 0x1b, | ||||
|   DW_TAG_inheritance = 0x1c, | ||||
|   DW_TAG_inlined_subroutine = 0x1d, | ||||
|   DW_TAG_module = 0x1e, | ||||
|   DW_TAG_ptr_to_member_type = 0x1f, | ||||
|   DW_TAG_set_type = 0x20, | ||||
|   DW_TAG_subrange_type = 0x21, | ||||
|   DW_TAG_with_stmt = 0x22, | ||||
|   DW_TAG_access_declaration = 0x23, | ||||
|   DW_TAG_base_type = 0x24, | ||||
|   DW_TAG_catch_block = 0x25, | ||||
|   DW_TAG_const_type = 0x26, | ||||
|   DW_TAG_constant = 0x27, | ||||
|   DW_TAG_enumerator = 0x28, | ||||
|   DW_TAG_file_type = 0x29, | ||||
|   DW_TAG_friend = 0x2a, | ||||
|   DW_TAG_namelist = 0x2b, | ||||
|   DW_TAG_namelist_item = 0x2c, | ||||
|   DW_TAG_packed_type = 0x2d, | ||||
|   DW_TAG_subprogram = 0x2e, | ||||
|   DW_TAG_template_type_param = 0x2f, | ||||
|   DW_TAG_template_value_param = 0x30, | ||||
|   DW_TAG_thrown_type = 0x31, | ||||
|   DW_TAG_try_block = 0x32, | ||||
|   DW_TAG_variant_part = 0x33, | ||||
|   DW_TAG_variable = 0x34, | ||||
|   DW_TAG_volatile_type = 0x35, | ||||
|   // DWARF 3. | ||||
|   DW_TAG_dwarf_procedure = 0x36, | ||||
|   DW_TAG_restrict_type = 0x37, | ||||
|   DW_TAG_interface_type = 0x38, | ||||
|   DW_TAG_namespace = 0x39, | ||||
|   DW_TAG_imported_module = 0x3a, | ||||
|   DW_TAG_unspecified_type = 0x3b, | ||||
|   DW_TAG_partial_unit = 0x3c, | ||||
|   DW_TAG_imported_unit = 0x3d, | ||||
|   // SGI/MIPS Extensions. | ||||
|   DW_TAG_MIPS_loop = 0x4081, | ||||
|   // HP extensions.  See: | ||||
|   // ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz | ||||
|   DW_TAG_HP_array_descriptor = 0x4090, | ||||
|   // GNU extensions. | ||||
|   DW_TAG_format_label = 0x4101,  // For FORTRAN 77 and Fortran 90. | ||||
|   DW_TAG_function_template = 0x4102,  // For C++. | ||||
|   DW_TAG_class_template = 0x4103,  // For C++. | ||||
|   DW_TAG_GNU_BINCL = 0x4104, | ||||
|   DW_TAG_GNU_EINCL = 0x4105, | ||||
|   // Extensions for UPC.  See: http://upc.gwu.edu/~upc. | ||||
|   DW_TAG_upc_shared_type = 0x8765, | ||||
|   DW_TAG_upc_strict_type = 0x8766, | ||||
|   DW_TAG_upc_relaxed_type = 0x8767, | ||||
|   // PGI (STMicroelectronics) extensions.  No documentation available. | ||||
|   DW_TAG_PGI_kanji_type      = 0xA000, | ||||
|   DW_TAG_PGI_interface_block = 0xA020 | ||||
| }; | ||||
|  | ||||
|  | ||||
| enum DwarfHasChild { | ||||
|   DW_children_no = 0, | ||||
|   DW_children_yes = 1 | ||||
| }; | ||||
|  | ||||
| // Form names and codes. | ||||
| enum DwarfForm { | ||||
|   DW_FORM_addr = 0x01, | ||||
|   DW_FORM_block2 = 0x03, | ||||
|   DW_FORM_block4 = 0x04, | ||||
|   DW_FORM_data2 = 0x05, | ||||
|   DW_FORM_data4 = 0x06, | ||||
|   DW_FORM_data8 = 0x07, | ||||
|   DW_FORM_string = 0x08, | ||||
|   DW_FORM_block = 0x09, | ||||
|   DW_FORM_block1 = 0x0a, | ||||
|   DW_FORM_data1 = 0x0b, | ||||
|   DW_FORM_flag = 0x0c, | ||||
|   DW_FORM_sdata = 0x0d, | ||||
|   DW_FORM_strp = 0x0e, | ||||
|   DW_FORM_udata = 0x0f, | ||||
|   DW_FORM_ref_addr = 0x10, | ||||
|   DW_FORM_ref1 = 0x11, | ||||
|   DW_FORM_ref2 = 0x12, | ||||
|   DW_FORM_ref4 = 0x13, | ||||
|   DW_FORM_ref8 = 0x14, | ||||
|   DW_FORM_ref_udata = 0x15, | ||||
|   DW_FORM_indirect = 0x16, | ||||
|  | ||||
|   // Added in DWARF 4: | ||||
|   DW_FORM_sec_offset = 0x17, | ||||
|   DW_FORM_exprloc = 0x18, | ||||
|   DW_FORM_flag_present = 0x19, | ||||
|   DW_FORM_ref_sig8 = 0x20 | ||||
| }; | ||||
|  | ||||
| // Attribute names and codes | ||||
| enum DwarfAttribute { | ||||
|   DW_AT_sibling = 0x01, | ||||
|   DW_AT_location = 0x02, | ||||
|   DW_AT_name = 0x03, | ||||
|   DW_AT_ordering = 0x09, | ||||
|   DW_AT_subscr_data = 0x0a, | ||||
|   DW_AT_byte_size = 0x0b, | ||||
|   DW_AT_bit_offset = 0x0c, | ||||
|   DW_AT_bit_size = 0x0d, | ||||
|   DW_AT_element_list = 0x0f, | ||||
|   DW_AT_stmt_list = 0x10, | ||||
|   DW_AT_low_pc = 0x11, | ||||
|   DW_AT_high_pc = 0x12, | ||||
|   DW_AT_language = 0x13, | ||||
|   DW_AT_member = 0x14, | ||||
|   DW_AT_discr = 0x15, | ||||
|   DW_AT_discr_value = 0x16, | ||||
|   DW_AT_visibility = 0x17, | ||||
|   DW_AT_import = 0x18, | ||||
|   DW_AT_string_length = 0x19, | ||||
|   DW_AT_common_reference = 0x1a, | ||||
|   DW_AT_comp_dir = 0x1b, | ||||
|   DW_AT_const_value = 0x1c, | ||||
|   DW_AT_containing_type = 0x1d, | ||||
|   DW_AT_default_value = 0x1e, | ||||
|   DW_AT_inline = 0x20, | ||||
|   DW_AT_is_optional = 0x21, | ||||
|   DW_AT_lower_bound = 0x22, | ||||
|   DW_AT_producer = 0x25, | ||||
|   DW_AT_prototyped = 0x27, | ||||
|   DW_AT_return_addr = 0x2a, | ||||
|   DW_AT_start_scope = 0x2c, | ||||
|   DW_AT_stride_size = 0x2e, | ||||
|   DW_AT_upper_bound = 0x2f, | ||||
|   DW_AT_abstract_origin = 0x31, | ||||
|   DW_AT_accessibility = 0x32, | ||||
|   DW_AT_address_class = 0x33, | ||||
|   DW_AT_artificial = 0x34, | ||||
|   DW_AT_base_types = 0x35, | ||||
|   DW_AT_calling_convention = 0x36, | ||||
|   DW_AT_count = 0x37, | ||||
|   DW_AT_data_member_location = 0x38, | ||||
|   DW_AT_decl_column = 0x39, | ||||
|   DW_AT_decl_file = 0x3a, | ||||
|   DW_AT_decl_line = 0x3b, | ||||
|   DW_AT_declaration = 0x3c, | ||||
|   DW_AT_discr_list = 0x3d, | ||||
|   DW_AT_encoding = 0x3e, | ||||
|   DW_AT_external = 0x3f, | ||||
|   DW_AT_frame_base = 0x40, | ||||
|   DW_AT_friend = 0x41, | ||||
|   DW_AT_identifier_case = 0x42, | ||||
|   DW_AT_macro_info = 0x43, | ||||
|   DW_AT_namelist_items = 0x44, | ||||
|   DW_AT_priority = 0x45, | ||||
|   DW_AT_segment = 0x46, | ||||
|   DW_AT_specification = 0x47, | ||||
|   DW_AT_static_link = 0x48, | ||||
|   DW_AT_type = 0x49, | ||||
|   DW_AT_use_location = 0x4a, | ||||
|   DW_AT_variable_parameter = 0x4b, | ||||
|   DW_AT_virtuality = 0x4c, | ||||
|   DW_AT_vtable_elem_location = 0x4d, | ||||
|   // DWARF 3 values. | ||||
|   DW_AT_allocated     = 0x4e, | ||||
|   DW_AT_associated    = 0x4f, | ||||
|   DW_AT_data_location = 0x50, | ||||
|   DW_AT_stride        = 0x51, | ||||
|   DW_AT_entry_pc      = 0x52, | ||||
|   DW_AT_use_UTF8      = 0x53, | ||||
|   DW_AT_extension     = 0x54, | ||||
|   DW_AT_ranges        = 0x55, | ||||
|   DW_AT_trampoline    = 0x56, | ||||
|   DW_AT_call_column   = 0x57, | ||||
|   DW_AT_call_file     = 0x58, | ||||
|   DW_AT_call_line     = 0x59, | ||||
|   // SGI/MIPS extensions. | ||||
|   DW_AT_MIPS_fde = 0x2001, | ||||
|   DW_AT_MIPS_loop_begin = 0x2002, | ||||
|   DW_AT_MIPS_tail_loop_begin = 0x2003, | ||||
|   DW_AT_MIPS_epilog_begin = 0x2004, | ||||
|   DW_AT_MIPS_loop_unroll_factor = 0x2005, | ||||
|   DW_AT_MIPS_software_pipeline_depth = 0x2006, | ||||
|   DW_AT_MIPS_linkage_name = 0x2007, | ||||
|   DW_AT_MIPS_stride = 0x2008, | ||||
|   DW_AT_MIPS_abstract_name = 0x2009, | ||||
|   DW_AT_MIPS_clone_origin = 0x200a, | ||||
|   DW_AT_MIPS_has_inlines = 0x200b, | ||||
|   // HP extensions. | ||||
|   DW_AT_HP_block_index         = 0x2000, | ||||
|   DW_AT_HP_unmodifiable        = 0x2001,  // Same as DW_AT_MIPS_fde. | ||||
|   DW_AT_HP_actuals_stmt_list   = 0x2010, | ||||
|   DW_AT_HP_proc_per_section    = 0x2011, | ||||
|   DW_AT_HP_raw_data_ptr        = 0x2012, | ||||
|   DW_AT_HP_pass_by_reference   = 0x2013, | ||||
|   DW_AT_HP_opt_level           = 0x2014, | ||||
|   DW_AT_HP_prof_version_id     = 0x2015, | ||||
|   DW_AT_HP_opt_flags           = 0x2016, | ||||
|   DW_AT_HP_cold_region_low_pc  = 0x2017, | ||||
|   DW_AT_HP_cold_region_high_pc = 0x2018, | ||||
|   DW_AT_HP_all_variables_modifiable = 0x2019, | ||||
|   DW_AT_HP_linkage_name        = 0x201a, | ||||
|   DW_AT_HP_prof_flags          = 0x201b,  // In comp unit of procs_info for -g. | ||||
|   // GNU extensions. | ||||
|   DW_AT_sf_names   = 0x2101, | ||||
|   DW_AT_src_info   = 0x2102, | ||||
|   DW_AT_mac_info   = 0x2103, | ||||
|   DW_AT_src_coords = 0x2104, | ||||
|   DW_AT_body_begin = 0x2105, | ||||
|   DW_AT_body_end   = 0x2106, | ||||
|   DW_AT_GNU_vector = 0x2107, | ||||
|   // VMS extensions. | ||||
|   DW_AT_VMS_rtnbeg_pd_address = 0x2201, | ||||
|   // UPC extension. | ||||
|   DW_AT_upc_threads_scaled = 0x3210, | ||||
|   // PGI (STMicroelectronics) extensions. | ||||
|   DW_AT_PGI_lbase    = 0x3a00, | ||||
|   DW_AT_PGI_soffset  = 0x3a01, | ||||
|   DW_AT_PGI_lstride  = 0x3a02 | ||||
| }; | ||||
|  | ||||
|  | ||||
| // Line number opcodes. | ||||
| enum DwarfLineNumberOps { | ||||
|   DW_LNS_extended_op = 0, | ||||
|   DW_LNS_copy = 1, | ||||
|   DW_LNS_advance_pc = 2, | ||||
|   DW_LNS_advance_line = 3, | ||||
|   DW_LNS_set_file = 4, | ||||
|   DW_LNS_set_column = 5, | ||||
|   DW_LNS_negate_stmt = 6, | ||||
|   DW_LNS_set_basic_block = 7, | ||||
|   DW_LNS_const_add_pc = 8, | ||||
|   DW_LNS_fixed_advance_pc = 9, | ||||
|   // DWARF 3. | ||||
|   DW_LNS_set_prologue_end = 10, | ||||
|   DW_LNS_set_epilogue_begin = 11, | ||||
|   DW_LNS_set_isa = 12 | ||||
| }; | ||||
|  | ||||
| // Line number extended opcodes. | ||||
| enum DwarfLineNumberExtendedOps { | ||||
|   DW_LNE_end_sequence = 1, | ||||
|   DW_LNE_set_address = 2, | ||||
|   DW_LNE_define_file = 3, | ||||
|   // HP extensions. | ||||
|   DW_LNE_HP_negate_is_UV_update      = 0x11, | ||||
|   DW_LNE_HP_push_context             = 0x12, | ||||
|   DW_LNE_HP_pop_context              = 0x13, | ||||
|   DW_LNE_HP_set_file_line_column     = 0x14, | ||||
|   DW_LNE_HP_set_routine_name         = 0x15, | ||||
|   DW_LNE_HP_set_sequence             = 0x16, | ||||
|   DW_LNE_HP_negate_post_semantics    = 0x17, | ||||
|   DW_LNE_HP_negate_function_exit     = 0x18, | ||||
|   DW_LNE_HP_negate_front_end_logical = 0x19, | ||||
|   DW_LNE_HP_define_proc              = 0x20 | ||||
| }; | ||||
|  | ||||
| // Type encoding names and codes | ||||
| enum DwarfEncoding { | ||||
|   DW_ATE_address                     =0x1, | ||||
|   DW_ATE_boolean                     =0x2, | ||||
|   DW_ATE_complex_float               =0x3, | ||||
|   DW_ATE_float                       =0x4, | ||||
|   DW_ATE_signed                      =0x5, | ||||
|   DW_ATE_signed_char                 =0x6, | ||||
|   DW_ATE_unsigned                    =0x7, | ||||
|   DW_ATE_unsigned_char               =0x8, | ||||
|   // DWARF3/DWARF3f | ||||
|   DW_ATE_imaginary_float             =0x9, | ||||
|   DW_ATE_packed_decimal              =0xa, | ||||
|   DW_ATE_numeric_string              =0xb, | ||||
|   DW_ATE_edited                      =0xc, | ||||
|   DW_ATE_signed_fixed                =0xd, | ||||
|   DW_ATE_unsigned_fixed              =0xe, | ||||
|   DW_ATE_decimal_float               =0xf, | ||||
|   DW_ATE_lo_user                     =0x80, | ||||
|   DW_ATE_hi_user                     =0xff | ||||
| }; | ||||
|  | ||||
| // Location virtual machine opcodes | ||||
| enum DwarfOpcode { | ||||
|   DW_OP_addr                         =0x03, | ||||
|   DW_OP_deref                        =0x06, | ||||
|   DW_OP_const1u                      =0x08, | ||||
|   DW_OP_const1s                      =0x09, | ||||
|   DW_OP_const2u                      =0x0a, | ||||
|   DW_OP_const2s                      =0x0b, | ||||
|   DW_OP_const4u                      =0x0c, | ||||
|   DW_OP_const4s                      =0x0d, | ||||
|   DW_OP_const8u                      =0x0e, | ||||
|   DW_OP_const8s                      =0x0f, | ||||
|   DW_OP_constu                       =0x10, | ||||
|   DW_OP_consts                       =0x11, | ||||
|   DW_OP_dup                          =0x12, | ||||
|   DW_OP_drop                         =0x13, | ||||
|   DW_OP_over                         =0x14, | ||||
|   DW_OP_pick                         =0x15, | ||||
|   DW_OP_swap                         =0x16, | ||||
|   DW_OP_rot                          =0x17, | ||||
|   DW_OP_xderef                       =0x18, | ||||
|   DW_OP_abs                          =0x19, | ||||
|   DW_OP_and                          =0x1a, | ||||
|   DW_OP_div                          =0x1b, | ||||
|   DW_OP_minus                        =0x1c, | ||||
|   DW_OP_mod                          =0x1d, | ||||
|   DW_OP_mul                          =0x1e, | ||||
|   DW_OP_neg                          =0x1f, | ||||
|   DW_OP_not                          =0x20, | ||||
|   DW_OP_or                           =0x21, | ||||
|   DW_OP_plus                         =0x22, | ||||
|   DW_OP_plus_uconst                  =0x23, | ||||
|   DW_OP_shl                          =0x24, | ||||
|   DW_OP_shr                          =0x25, | ||||
|   DW_OP_shra                         =0x26, | ||||
|   DW_OP_xor                          =0x27, | ||||
|   DW_OP_bra                          =0x28, | ||||
|   DW_OP_eq                           =0x29, | ||||
|   DW_OP_ge                           =0x2a, | ||||
|   DW_OP_gt                           =0x2b, | ||||
|   DW_OP_le                           =0x2c, | ||||
|   DW_OP_lt                           =0x2d, | ||||
|   DW_OP_ne                           =0x2e, | ||||
|   DW_OP_skip                         =0x2f, | ||||
|   DW_OP_lit0                         =0x30, | ||||
|   DW_OP_lit1                         =0x31, | ||||
|   DW_OP_lit2                         =0x32, | ||||
|   DW_OP_lit3                         =0x33, | ||||
|   DW_OP_lit4                         =0x34, | ||||
|   DW_OP_lit5                         =0x35, | ||||
|   DW_OP_lit6                         =0x36, | ||||
|   DW_OP_lit7                         =0x37, | ||||
|   DW_OP_lit8                         =0x38, | ||||
|   DW_OP_lit9                         =0x39, | ||||
|   DW_OP_lit10                        =0x3a, | ||||
|   DW_OP_lit11                        =0x3b, | ||||
|   DW_OP_lit12                        =0x3c, | ||||
|   DW_OP_lit13                        =0x3d, | ||||
|   DW_OP_lit14                        =0x3e, | ||||
|   DW_OP_lit15                        =0x3f, | ||||
|   DW_OP_lit16                        =0x40, | ||||
|   DW_OP_lit17                        =0x41, | ||||
|   DW_OP_lit18                        =0x42, | ||||
|   DW_OP_lit19                        =0x43, | ||||
|   DW_OP_lit20                        =0x44, | ||||
|   DW_OP_lit21                        =0x45, | ||||
|   DW_OP_lit22                        =0x46, | ||||
|   DW_OP_lit23                        =0x47, | ||||
|   DW_OP_lit24                        =0x48, | ||||
|   DW_OP_lit25                        =0x49, | ||||
|   DW_OP_lit26                        =0x4a, | ||||
|   DW_OP_lit27                        =0x4b, | ||||
|   DW_OP_lit28                        =0x4c, | ||||
|   DW_OP_lit29                        =0x4d, | ||||
|   DW_OP_lit30                        =0x4e, | ||||
|   DW_OP_lit31                        =0x4f, | ||||
|   DW_OP_reg0                         =0x50, | ||||
|   DW_OP_reg1                         =0x51, | ||||
|   DW_OP_reg2                         =0x52, | ||||
|   DW_OP_reg3                         =0x53, | ||||
|   DW_OP_reg4                         =0x54, | ||||
|   DW_OP_reg5                         =0x55, | ||||
|   DW_OP_reg6                         =0x56, | ||||
|   DW_OP_reg7                         =0x57, | ||||
|   DW_OP_reg8                         =0x58, | ||||
|   DW_OP_reg9                         =0x59, | ||||
|   DW_OP_reg10                        =0x5a, | ||||
|   DW_OP_reg11                        =0x5b, | ||||
|   DW_OP_reg12                        =0x5c, | ||||
|   DW_OP_reg13                        =0x5d, | ||||
|   DW_OP_reg14                        =0x5e, | ||||
|   DW_OP_reg15                        =0x5f, | ||||
|   DW_OP_reg16                        =0x60, | ||||
|   DW_OP_reg17                        =0x61, | ||||
|   DW_OP_reg18                        =0x62, | ||||
|   DW_OP_reg19                        =0x63, | ||||
|   DW_OP_reg20                        =0x64, | ||||
|   DW_OP_reg21                        =0x65, | ||||
|   DW_OP_reg22                        =0x66, | ||||
|   DW_OP_reg23                        =0x67, | ||||
|   DW_OP_reg24                        =0x68, | ||||
|   DW_OP_reg25                        =0x69, | ||||
|   DW_OP_reg26                        =0x6a, | ||||
|   DW_OP_reg27                        =0x6b, | ||||
|   DW_OP_reg28                        =0x6c, | ||||
|   DW_OP_reg29                        =0x6d, | ||||
|   DW_OP_reg30                        =0x6e, | ||||
|   DW_OP_reg31                        =0x6f, | ||||
|   DW_OP_breg0                        =0x70, | ||||
|   DW_OP_breg1                        =0x71, | ||||
|   DW_OP_breg2                        =0x72, | ||||
|   DW_OP_breg3                        =0x73, | ||||
|   DW_OP_breg4                        =0x74, | ||||
|   DW_OP_breg5                        =0x75, | ||||
|   DW_OP_breg6                        =0x76, | ||||
|   DW_OP_breg7                        =0x77, | ||||
|   DW_OP_breg8                        =0x78, | ||||
|   DW_OP_breg9                        =0x79, | ||||
|   DW_OP_breg10                       =0x7a, | ||||
|   DW_OP_breg11                       =0x7b, | ||||
|   DW_OP_breg12                       =0x7c, | ||||
|   DW_OP_breg13                       =0x7d, | ||||
|   DW_OP_breg14                       =0x7e, | ||||
|   DW_OP_breg15                       =0x7f, | ||||
|   DW_OP_breg16                       =0x80, | ||||
|   DW_OP_breg17                       =0x81, | ||||
|   DW_OP_breg18                       =0x82, | ||||
|   DW_OP_breg19                       =0x83, | ||||
|   DW_OP_breg20                       =0x84, | ||||
|   DW_OP_breg21                       =0x85, | ||||
|   DW_OP_breg22                       =0x86, | ||||
|   DW_OP_breg23                       =0x87, | ||||
|   DW_OP_breg24                       =0x88, | ||||
|   DW_OP_breg25                       =0x89, | ||||
|   DW_OP_breg26                       =0x8a, | ||||
|   DW_OP_breg27                       =0x8b, | ||||
|   DW_OP_breg28                       =0x8c, | ||||
|   DW_OP_breg29                       =0x8d, | ||||
|   DW_OP_breg30                       =0x8e, | ||||
|   DW_OP_breg31                       =0x8f, | ||||
|   DW_OP_regX                         =0x90, | ||||
|   DW_OP_fbreg                        =0x91, | ||||
|   DW_OP_bregX                        =0x92, | ||||
|   DW_OP_piece                        =0x93, | ||||
|   DW_OP_deref_size                   =0x94, | ||||
|   DW_OP_xderef_size                  =0x95, | ||||
|   DW_OP_nop                          =0x96, | ||||
|   // DWARF3/DWARF3f | ||||
|   DW_OP_push_object_address          =0x97, | ||||
|   DW_OP_call2                        =0x98, | ||||
|   DW_OP_call4                        =0x99, | ||||
|   DW_OP_call_ref                     =0x9a, | ||||
|   DW_OP_form_tls_address             =0x9b, | ||||
|   DW_OP_call_frame_cfa               =0x9c, | ||||
|   DW_OP_bit_piece                    =0x9d, | ||||
|   DW_OP_lo_user                      =0xe0, | ||||
|   DW_OP_hi_user                      =0xff,   | ||||
|   // GNU extensions | ||||
|   DW_OP_GNU_push_tls_address         =0xe0 | ||||
| }; | ||||
|  | ||||
| // Source languages.  These are values for DW_AT_language. | ||||
| enum DwarfLanguage | ||||
|   { | ||||
|     DW_LANG_none                     =0x0000, | ||||
|     DW_LANG_C89                      =0x0001, | ||||
|     DW_LANG_C                        =0x0002, | ||||
|     DW_LANG_Ada83                    =0x0003, | ||||
|     DW_LANG_C_plus_plus              =0x0004, | ||||
|     DW_LANG_Cobol74                  =0x0005, | ||||
|     DW_LANG_Cobol85                  =0x0006, | ||||
|     DW_LANG_Fortran77                =0x0007, | ||||
|     DW_LANG_Fortran90                =0x0008, | ||||
|     DW_LANG_Pascal83                 =0x0009, | ||||
|     DW_LANG_Modula2                  =0x000a, | ||||
|     DW_LANG_Java                     =0x000b, | ||||
|     DW_LANG_C99                      =0x000c, | ||||
|     DW_LANG_Ada95                    =0x000d, | ||||
|     DW_LANG_Fortran95                =0x000e, | ||||
|     DW_LANG_PLI                      =0x000f, | ||||
|     DW_LANG_ObjC                     =0x0010, | ||||
|     DW_LANG_ObjC_plus_plus           =0x0011, | ||||
|     DW_LANG_UPC                      =0x0012, | ||||
|     DW_LANG_D                        =0x0013, | ||||
|     // Implementation-defined language code range. | ||||
|     DW_LANG_lo_user = 0x8000, | ||||
|     DW_LANG_hi_user = 0xffff, | ||||
|  | ||||
|     // Extensions. | ||||
|  | ||||
|     // MIPS assembly language.  The GNU toolchain uses this for all | ||||
|     // assembly languages, since there's no generic DW_LANG_ value for that. | ||||
|     // See include/dwarf2.h in the binutils, gdb, or gcc source trees. | ||||
|     DW_LANG_Mips_Assembler           =0x8001, | ||||
|     DW_LANG_Upc                      =0x8765 // Unified Parallel C | ||||
|   }; | ||||
|  | ||||
| // Inline codes.  These are values for DW_AT_inline. | ||||
| enum DwarfInline { | ||||
|   DW_INL_not_inlined                 =0x0, | ||||
|   DW_INL_inlined                     =0x1, | ||||
|   DW_INL_declared_not_inlined        =0x2, | ||||
|   DW_INL_declared_inlined            =0x3 | ||||
| }; | ||||
|  | ||||
| // Call Frame Info instructions. | ||||
| enum DwarfCFI | ||||
|   { | ||||
|     DW_CFA_advance_loc        = 0x40, | ||||
|     DW_CFA_offset             = 0x80, | ||||
|     DW_CFA_restore            = 0xc0, | ||||
|     DW_CFA_nop                = 0x00, | ||||
|     DW_CFA_set_loc            = 0x01, | ||||
|     DW_CFA_advance_loc1       = 0x02, | ||||
|     DW_CFA_advance_loc2       = 0x03, | ||||
|     DW_CFA_advance_loc4       = 0x04, | ||||
|     DW_CFA_offset_extended    = 0x05, | ||||
|     DW_CFA_restore_extended   = 0x06, | ||||
|     DW_CFA_undefined          = 0x07, | ||||
|     DW_CFA_same_value         = 0x08, | ||||
|     DW_CFA_register           = 0x09, | ||||
|     DW_CFA_remember_state     = 0x0a, | ||||
|     DW_CFA_restore_state      = 0x0b, | ||||
|     DW_CFA_def_cfa            = 0x0c, | ||||
|     DW_CFA_def_cfa_register   = 0x0d, | ||||
|     DW_CFA_def_cfa_offset     = 0x0e, | ||||
|     DW_CFA_def_cfa_expression = 0x0f, | ||||
|     DW_CFA_expression         = 0x10, | ||||
|     DW_CFA_offset_extended_sf = 0x11, | ||||
|     DW_CFA_def_cfa_sf         = 0x12, | ||||
|     DW_CFA_def_cfa_offset_sf  = 0x13, | ||||
|     DW_CFA_val_offset         = 0x14, | ||||
|     DW_CFA_val_offset_sf      = 0x15, | ||||
|     DW_CFA_val_expression     = 0x16, | ||||
|  | ||||
|     // Opcodes in this range are reserved for user extensions. | ||||
|     DW_CFA_lo_user = 0x1c, | ||||
|     DW_CFA_hi_user = 0x3f, | ||||
|  | ||||
|     // SGI/MIPS specific. | ||||
|     DW_CFA_MIPS_advance_loc8 = 0x1d, | ||||
|  | ||||
|     // GNU extensions. | ||||
|     DW_CFA_GNU_window_save = 0x2d, | ||||
|     DW_CFA_GNU_args_size = 0x2e, | ||||
|     DW_CFA_GNU_negative_offset_extended = 0x2f | ||||
|   }; | ||||
|  | ||||
| // Exception handling 'z' augmentation letters. | ||||
| enum DwarfZAugmentationCodes { | ||||
|   // If the CFI augmentation string begins with 'z', then the CIE and FDE | ||||
|   // have an augmentation data area just before the instructions, whose | ||||
|   // contents are determined by the subsequent augmentation letters. | ||||
|   DW_Z_augmentation_start = 'z', | ||||
|  | ||||
|   // If this letter is present in a 'z' augmentation string, the CIE | ||||
|   // augmentation data includes a pointer encoding, and the FDE | ||||
|   // augmentation data includes a language-specific data area pointer, | ||||
|   // represented using that encoding. | ||||
|   DW_Z_has_LSDA = 'L', | ||||
|  | ||||
|   // If this letter is present in a 'z' augmentation string, the CIE | ||||
|   // augmentation data includes a pointer encoding, followed by a pointer | ||||
|   // to a personality routine, represented using that encoding. | ||||
|   DW_Z_has_personality_routine = 'P', | ||||
|  | ||||
|   // If this letter is present in a 'z' augmentation string, the CIE | ||||
|   // augmentation data includes a pointer encoding describing how the FDE's | ||||
|   // initial location, address range, and DW_CFA_set_loc operands are | ||||
|   // encoded. | ||||
|   DW_Z_has_FDE_address_encoding = 'R', | ||||
|  | ||||
|   // If this letter is present in a 'z' augmentation string, then code | ||||
|   // addresses covered by FDEs that cite this CIE are signal delivery | ||||
|   // trampolines. Return addresses of frames in trampolines should not be | ||||
|   // adjusted as described in section 6.4.4 of the DWARF 3 spec. | ||||
|   DW_Z_is_signal_trampoline = 'S' | ||||
| }; | ||||
|  | ||||
| // Exception handling frame description pointer formats, as described | ||||
| // by the Linux Standard Base Core Specification 4.0, section 11.5, | ||||
| // DWARF Extensions. | ||||
| enum DwarfPointerEncoding | ||||
|   { | ||||
|     DW_EH_PE_absptr	= 0x00, | ||||
|     DW_EH_PE_omit	= 0xff, | ||||
|     DW_EH_PE_uleb128    = 0x01, | ||||
|     DW_EH_PE_udata2	= 0x02, | ||||
|     DW_EH_PE_udata4	= 0x03, | ||||
|     DW_EH_PE_udata8	= 0x04, | ||||
|     DW_EH_PE_sleb128    = 0x09, | ||||
|     DW_EH_PE_sdata2	= 0x0A, | ||||
|     DW_EH_PE_sdata4	= 0x0B, | ||||
|     DW_EH_PE_sdata8	= 0x0C, | ||||
|     DW_EH_PE_pcrel	= 0x10, | ||||
|     DW_EH_PE_textrel	= 0x20, | ||||
|     DW_EH_PE_datarel	= 0x30, | ||||
|     DW_EH_PE_funcrel	= 0x40, | ||||
|     DW_EH_PE_aligned	= 0x50, | ||||
|  | ||||
|     // The GNU toolchain sources define this enum value as well, | ||||
|     // simply to help classify the lower nybble values into signed and | ||||
|     // unsigned groups. | ||||
|     DW_EH_PE_signed	= 0x08, | ||||
|  | ||||
|     // This is not documented in LSB 4.0, but it is used in both the | ||||
|     // Linux and OS X toolchains. It can be added to any other | ||||
|     // encoding (except DW_EH_PE_aligned), and indicates that the | ||||
|     // encoded value represents the address at which the true address | ||||
|     // is stored, not the true address itself. | ||||
|     DW_EH_PE_indirect	= 0x80   | ||||
|   }; | ||||
|  | ||||
| }  // namespace dwarf2reader | ||||
| #endif  // COMMON_DWARF_DWARF2ENUMS_H__ | ||||
							
								
								
									
										2343
									
								
								sdk/breakpad/common/dwarf/dwarf2reader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2343
									
								
								sdk/breakpad/common/dwarf/dwarf2reader.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										1050
									
								
								sdk/breakpad/common/dwarf/dwarf2reader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1050
									
								
								sdk/breakpad/common/dwarf/dwarf2reader.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2452
									
								
								sdk/breakpad/common/dwarf/dwarf2reader_cfi_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2452
									
								
								sdk/breakpad/common/dwarf/dwarf2reader_cfi_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										484
									
								
								sdk/breakpad/common/dwarf/dwarf2reader_die_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										484
									
								
								sdk/breakpad/common/dwarf/dwarf2reader_die_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,484 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit | ||||
|  | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include <iostream> | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/dwarf/bytereader-inl.h" | ||||
| #include "common/dwarf/dwarf2reader_test_common.h" | ||||
| #include "common/dwarf/dwarf2reader.h" | ||||
| #include "common/using_std_string.h" | ||||
| #include "google_breakpad/common/breakpad_types.h" | ||||
|  | ||||
| using google_breakpad::test_assembler::Endianness; | ||||
| using google_breakpad::test_assembler::Label; | ||||
| using google_breakpad::test_assembler::Section; | ||||
| using google_breakpad::test_assembler::kBigEndian; | ||||
| using google_breakpad::test_assembler::kLittleEndian; | ||||
|  | ||||
| using dwarf2reader::ByteReader; | ||||
| using dwarf2reader::CompilationUnit; | ||||
| using dwarf2reader::Dwarf2Handler; | ||||
| using dwarf2reader::DwarfAttribute; | ||||
| using dwarf2reader::DwarfForm; | ||||
| using dwarf2reader::DwarfHasChild; | ||||
| using dwarf2reader::DwarfTag; | ||||
| using dwarf2reader::ENDIANNESS_BIG; | ||||
| using dwarf2reader::ENDIANNESS_LITTLE; | ||||
| using dwarf2reader::SectionMap; | ||||
|  | ||||
| using std::vector; | ||||
| using testing::InSequence; | ||||
| using testing::Pointee; | ||||
| using testing::Return; | ||||
| using testing::Sequence; | ||||
| using testing::Test; | ||||
| using testing::TestWithParam; | ||||
| using testing::_; | ||||
|  | ||||
| class MockDwarf2Handler: public Dwarf2Handler { | ||||
|  public: | ||||
|   MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size, | ||||
|                                           uint8 offset_size, uint64 cu_length, | ||||
|                                           uint8 dwarf_version)); | ||||
|   MOCK_METHOD2(StartDIE, bool(uint64 offset, enum DwarfTag tag)); | ||||
|   MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset, | ||||
|                                               DwarfAttribute attr, | ||||
|                                               enum DwarfForm form, | ||||
|                                               uint64 data)); | ||||
|   MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset, | ||||
|                                             enum DwarfAttribute attr, | ||||
|                                             enum DwarfForm form, | ||||
|                                             int64 data)); | ||||
|   MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset, | ||||
|                                                enum DwarfAttribute attr, | ||||
|                                                enum DwarfForm form, | ||||
|                                                uint64 data)); | ||||
|   MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset, | ||||
|                                             enum DwarfAttribute attr, | ||||
|                                             enum DwarfForm form, | ||||
|                                             const char* data, | ||||
|                                             uint64 len)); | ||||
|   MOCK_METHOD4(ProcessAttributeString, void(uint64 offset, | ||||
|                                             enum DwarfAttribute attr, | ||||
|                                             enum DwarfForm form, | ||||
|                                             const string& data)); | ||||
|   MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset, | ||||
|                                                DwarfAttribute attr, | ||||
|                                                enum DwarfForm form, | ||||
|                                                uint64 signature)); | ||||
|   MOCK_METHOD1(EndDIE, void(uint64 offset)); | ||||
| }; | ||||
|  | ||||
| struct DIEFixture { | ||||
|  | ||||
|   DIEFixture() { | ||||
|     // Fix the initial offset of the .debug_info and .debug_abbrev sections. | ||||
|     info.start() = 0; | ||||
|     abbrevs.start() = 0; | ||||
|  | ||||
|     // Default expectations for the data handler. | ||||
|     EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0); | ||||
|     EXPECT_CALL(handler, StartDIE(_, _)).Times(0); | ||||
|     EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0); | ||||
|     EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0); | ||||
|     EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0); | ||||
|     EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0); | ||||
|     EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0); | ||||
|     EXPECT_CALL(handler, EndDIE(_)).Times(0); | ||||
|   } | ||||
|  | ||||
|   // Return a reference to a section map whose .debug_info section refers | ||||
|   // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This | ||||
|   // function returns a reference to the same SectionMap each time; new | ||||
|   // calls wipe out maps established by earlier calls. | ||||
|   const SectionMap &MakeSectionMap() { | ||||
|     // Copy the sections' contents into strings that will live as long as | ||||
|     // the map itself. | ||||
|     assert(info.GetContents(&info_contents)); | ||||
|     assert(abbrevs.GetContents(&abbrevs_contents)); | ||||
|     section_map.clear(); | ||||
|     section_map[".debug_info"].first  = info_contents.data(); | ||||
|     section_map[".debug_info"].second = info_contents.size(); | ||||
|     section_map[".debug_abbrev"].first  = abbrevs_contents.data(); | ||||
|     section_map[".debug_abbrev"].second = abbrevs_contents.size(); | ||||
|     return section_map; | ||||
|   } | ||||
|  | ||||
|   TestCompilationUnit info; | ||||
|   TestAbbrevTable abbrevs; | ||||
|   MockDwarf2Handler handler; | ||||
|   string abbrevs_contents, info_contents; | ||||
|   SectionMap section_map; | ||||
| }; | ||||
|  | ||||
| struct DwarfHeaderParams { | ||||
|   DwarfHeaderParams(Endianness endianness, size_t format_size, | ||||
|                    int version, size_t address_size) | ||||
|       : endianness(endianness), format_size(format_size), | ||||
|         version(version), address_size(address_size) { } | ||||
|   Endianness endianness; | ||||
|   size_t format_size;                   // 4-byte or 8-byte DWARF offsets | ||||
|   int version; | ||||
|   size_t address_size; | ||||
| }; | ||||
|  | ||||
| class DwarfHeader: public DIEFixture, | ||||
|                    public TestWithParam<DwarfHeaderParams> { }; | ||||
|  | ||||
| TEST_P(DwarfHeader, Header) { | ||||
|   Label abbrev_table = abbrevs.Here(); | ||||
|   abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit, | ||||
|                  dwarf2reader::DW_children_yes) | ||||
|       .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string) | ||||
|       .EndAbbrev() | ||||
|       .EndTable(); | ||||
|  | ||||
|   info.set_format_size(GetParam().format_size); | ||||
|   info.set_endianness(GetParam().endianness); | ||||
|  | ||||
|   info.Header(GetParam().version, abbrev_table, GetParam().address_size) | ||||
|       .ULEB128(1)                     // DW_TAG_compile_unit, with children | ||||
|       .AppendCString("sam")           // DW_AT_name, DW_FORM_string | ||||
|       .D8(0);                         // end of children | ||||
|   info.Finish(); | ||||
|  | ||||
|   { | ||||
|     InSequence s; | ||||
|     EXPECT_CALL(handler, | ||||
|                 StartCompilationUnit(0, GetParam().address_size, | ||||
|                                      GetParam().format_size, _, | ||||
|                                      GetParam().version)) | ||||
|         .WillOnce(Return(true)); | ||||
|     EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit)) | ||||
|         .WillOnce(Return(true)); | ||||
|     EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name,  | ||||
|                                                 dwarf2reader::DW_FORM_string, | ||||
|                                                 "sam")) | ||||
|         .WillOnce(Return()); | ||||
|     EXPECT_CALL(handler, EndDIE(_)) | ||||
|         .WillOnce(Return()); | ||||
|   } | ||||
|  | ||||
|   ByteReader byte_reader(GetParam().endianness == kLittleEndian ? | ||||
|                          ENDIANNESS_LITTLE : ENDIANNESS_BIG); | ||||
|   CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); | ||||
|   EXPECT_EQ(parser.Start(), info_contents.size()); | ||||
| } | ||||
|  | ||||
| INSTANTIATE_TEST_CASE_P( | ||||
|     HeaderVariants, DwarfHeader, | ||||
|     ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 2, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 3, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 3, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 4, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 4, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 2, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 2, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 3, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 3, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 4, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 4, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 2, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 2, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 3, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 3, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 4, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 4, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 2, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 2, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 3, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 3, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 4, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 4, 8))); | ||||
|  | ||||
| struct DwarfFormsFixture: public DIEFixture { | ||||
|   // Start a compilation unit, as directed by |params|, containing one | ||||
|   // childless DIE of the given tag, with one attribute of the given name | ||||
|   // and form. The 'info' fixture member is left just after the abbrev | ||||
|   // code, waiting for the attribute value to be appended. | ||||
|   void StartSingleAttributeDIE(const DwarfHeaderParams ¶ms, | ||||
|                                DwarfTag tag, DwarfAttribute name, | ||||
|                                DwarfForm form) { | ||||
|     // Create the abbreviation table. | ||||
|     Label abbrev_table = abbrevs.Here(); | ||||
|     abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no) | ||||
|         .Attribute(name, form) | ||||
|         .EndAbbrev() | ||||
|         .EndTable(); | ||||
|  | ||||
|     // Create the compilation unit, up to the attribute value. | ||||
|     info.set_format_size(params.format_size); | ||||
|     info.set_endianness(params.endianness); | ||||
|     info.Header(params.version, abbrev_table, params.address_size) | ||||
|         .ULEB128(1);                    // abbrev code | ||||
|   } | ||||
|  | ||||
|   // Set up handler to expect a compilation unit matching |params|, | ||||
|   // containing one childless DIE of the given tag, in the sequence s. Stop | ||||
|   // just before the expectations. | ||||
|   void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, | ||||
|                                   DwarfTag tag, uint64 offset=0) { | ||||
|     EXPECT_CALL(handler, | ||||
|                 StartCompilationUnit(offset, params.address_size, | ||||
|                                      params.format_size, _, | ||||
|                                      params.version)) | ||||
|         .InSequence(s) | ||||
|         .WillOnce(Return(true)); | ||||
|     EXPECT_CALL(handler, StartDIE(_, tag)) | ||||
|         .InSequence(s) | ||||
|         .WillOnce(Return(true)); | ||||
|   } | ||||
|  | ||||
|   void ExpectEndCompilationUnit() { | ||||
|     EXPECT_CALL(handler, EndDIE(_)) | ||||
|         .InSequence(s) | ||||
|         .WillOnce(Return()); | ||||
|   } | ||||
|  | ||||
|   void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) { | ||||
|     ByteReader byte_reader(params.endianness == kLittleEndian ? | ||||
|                            ENDIANNESS_LITTLE : ENDIANNESS_BIG); | ||||
|     CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler); | ||||
|     EXPECT_EQ(offset + parser.Start(), info_contents.size()); | ||||
|   } | ||||
|  | ||||
|   // The sequence to which the fixture's methods append expectations. | ||||
|   Sequence s; | ||||
| }; | ||||
|  | ||||
| struct DwarfForms: public DwarfFormsFixture, | ||||
|                    public TestWithParam<DwarfHeaderParams> { }; | ||||
|  | ||||
| TEST_P(DwarfForms, addr) { | ||||
|   StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit, | ||||
|                           dwarf2reader::DW_AT_low_pc, | ||||
|                           dwarf2reader::DW_FORM_addr); | ||||
|   uint64_t value; | ||||
|   if (GetParam().address_size == 4) { | ||||
|     value = 0xc8e9ffcc; | ||||
|     info.D32(value); | ||||
|   } else { | ||||
|     value = 0xe942517fc2768564ULL; | ||||
|     info.D64(value); | ||||
|   } | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit); | ||||
|   EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc,  | ||||
|                                                 dwarf2reader::DW_FORM_addr, | ||||
|                                                 value)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam()); | ||||
| } | ||||
|  | ||||
| TEST_P(DwarfForms, block2_empty) { | ||||
|   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, | ||||
|                           (DwarfAttribute) 0xe52c4463, | ||||
|                           dwarf2reader::DW_FORM_block2); | ||||
|   info.D16(0); | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); | ||||
|   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, | ||||
|                                               dwarf2reader::DW_FORM_block2, | ||||
|                                               _, 0)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam()); | ||||
| } | ||||
|  | ||||
| TEST_P(DwarfForms, block2) { | ||||
|   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, | ||||
|                           (DwarfAttribute) 0xe52c4463, | ||||
|                           dwarf2reader::DW_FORM_block2); | ||||
|   unsigned char data[258]; | ||||
|   memset(data, '*', sizeof(data)); | ||||
|   info.D16(sizeof(data)) | ||||
|       .Append(data, sizeof(data)); | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); | ||||
|   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, | ||||
|                                               dwarf2reader::DW_FORM_block2, | ||||
|                                               Pointee('*'), 258)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam()); | ||||
| } | ||||
|  | ||||
| TEST_P(DwarfForms, flag_present) { | ||||
|   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2, | ||||
|                           (DwarfAttribute) 0x359d1972, | ||||
|                           dwarf2reader::DW_FORM_flag_present); | ||||
|   // DW_FORM_flag_present occupies no space in the DIE. | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2); | ||||
|   EXPECT_CALL(handler, | ||||
|               ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972, | ||||
|                                        dwarf2reader::DW_FORM_flag_present, | ||||
|                                        1)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam()); | ||||
| } | ||||
|  | ||||
| TEST_P(DwarfForms, sec_offset) { | ||||
|   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689, | ||||
|                           (DwarfAttribute) 0xa060bfd1, | ||||
|                           dwarf2reader::DW_FORM_sec_offset); | ||||
|   uint64_t value; | ||||
|   if (GetParam().format_size == 4) { | ||||
|     value = 0xacc9c388; | ||||
|     info.D32(value); | ||||
|   } else { | ||||
|     value = 0xcffe5696ffe3ed0aULL; | ||||
|     info.D64(value); | ||||
|   } | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689); | ||||
|   EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1, | ||||
|                                                 dwarf2reader::DW_FORM_sec_offset, | ||||
|                                                 value)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam()); | ||||
| } | ||||
|  | ||||
| TEST_P(DwarfForms, exprloc) { | ||||
|   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb, | ||||
|                           (DwarfAttribute) 0xba3ae5cb, | ||||
|                           dwarf2reader::DW_FORM_exprloc); | ||||
|   info.ULEB128(29) | ||||
|       .Append(29, 173); | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb); | ||||
|   EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb, | ||||
|                                               dwarf2reader::DW_FORM_exprloc, | ||||
|                                               Pointee(173), 29)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam()); | ||||
| } | ||||
|  | ||||
| TEST_P(DwarfForms, ref_sig8) { | ||||
|   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, | ||||
|                           (DwarfAttribute) 0xd708d908, | ||||
|                           dwarf2reader::DW_FORM_ref_sig8); | ||||
|   info.D64(0xf72fa0cb6ddcf9d6ULL); | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); | ||||
|   EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, | ||||
|                                                  dwarf2reader::DW_FORM_ref_sig8, | ||||
|                                                  0xf72fa0cb6ddcf9d6ULL)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam()); | ||||
| } | ||||
|  | ||||
| // A value passed to ProcessAttributeSignature is just an absolute number, | ||||
| // not an offset within the compilation unit as most of the other | ||||
| // DW_FORM_ref forms are. Check that the reader doesn't try to apply any | ||||
| // offset to the signature, by reading it from a compilation unit that does | ||||
| // not start at the beginning of the section. | ||||
| TEST_P(DwarfForms, ref_sig8_not_first) { | ||||
|   info.Append(98, '*'); | ||||
|   StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, | ||||
|                           (DwarfAttribute) 0xd708d908, | ||||
|                           dwarf2reader::DW_FORM_ref_sig8); | ||||
|   info.D64(0xf72fa0cb6ddcf9d6ULL); | ||||
|   info.Finish(); | ||||
|  | ||||
|   ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98); | ||||
|   EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, | ||||
|                                                  dwarf2reader::DW_FORM_ref_sig8, | ||||
|                                                  0xf72fa0cb6ddcf9d6ULL)) | ||||
|       .InSequence(s) | ||||
|       .WillOnce(Return()); | ||||
|   ExpectEndCompilationUnit(); | ||||
|  | ||||
|   ParseCompilationUnit(GetParam(), 98); | ||||
| } | ||||
|  | ||||
| // Tests for the other attribute forms could go here. | ||||
|  | ||||
| INSTANTIATE_TEST_CASE_P( | ||||
|     HeaderVariants, DwarfForms, | ||||
|     ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 2, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 3, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 3, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 4, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 4, 4, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 2, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 2, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 3, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 3, 8), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 4, 4), | ||||
|                       DwarfHeaderParams(kLittleEndian, 8, 4, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 2, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 2, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 3, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 3, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 4, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    4, 4, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 2, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 2, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 3, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 3, 8), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 4, 4), | ||||
|                       DwarfHeaderParams(kBigEndian,    8, 4, 8))); | ||||
							
								
								
									
										149
									
								
								sdk/breakpad/common/dwarf/dwarf2reader_test_common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								sdk/breakpad/common/dwarf/dwarf2reader_test_common.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf2reader_test_common.h: Define TestCompilationUnit and | ||||
| // TestAbbrevTable, classes for creating properly (and improperly) | ||||
| // formatted DWARF compilation unit data for unit tests. | ||||
|  | ||||
| #ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ | ||||
| #define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ | ||||
|  | ||||
| #include "common/test_assembler.h" | ||||
| #include "common/dwarf/dwarf2enums.h" | ||||
|  | ||||
| // A subclass of test_assembler::Section, specialized for constructing | ||||
| // DWARF compilation units. | ||||
| class TestCompilationUnit: public google_breakpad::test_assembler::Section { | ||||
|  public: | ||||
|   typedef dwarf2reader::DwarfTag DwarfTag; | ||||
|   typedef dwarf2reader::DwarfAttribute DwarfAttribute; | ||||
|   typedef dwarf2reader::DwarfForm DwarfForm; | ||||
|   typedef google_breakpad::test_assembler::Label Label; | ||||
|  | ||||
|   // Set the section's DWARF format size (the 32-bit DWARF format or the | ||||
|   // 64-bit DWARF format, for lengths and section offsets --- not the | ||||
|   // address size) to format_size. | ||||
|   void set_format_size(size_t format_size) { | ||||
|     assert(format_size == 4 || format_size == 8); | ||||
|     format_size_ = format_size; | ||||
|   } | ||||
|      | ||||
|   // Append a DWARF section offset value, of the appropriate size for this | ||||
|   // compilation unit. | ||||
|   template<typename T> | ||||
|   void SectionOffset(T offset) { | ||||
|     if (format_size_ == 4) | ||||
|       D32(offset); | ||||
|     else | ||||
|       D64(offset); | ||||
|   } | ||||
|  | ||||
|   // Append a DWARF compilation unit header to the section, with the given | ||||
|   // DWARF version, abbrev table offset, and address size. | ||||
|   TestCompilationUnit &Header(int version, const Label &abbrev_offset, | ||||
|                               size_t address_size) { | ||||
|     if (format_size_ == 4) { | ||||
|       D32(length_); | ||||
|     } else { | ||||
|       D32(0xffffffff); | ||||
|       D64(length_); | ||||
|     } | ||||
|     post_length_offset_ = Size(); | ||||
|     D16(version); | ||||
|     SectionOffset(abbrev_offset); | ||||
|     D8(address_size); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Mark the end of this header's DIEs. | ||||
|   TestCompilationUnit &Finish() { | ||||
|     length_ = Size() - post_length_offset_; | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   // The DWARF format size for this compilation unit. | ||||
|   size_t format_size_; | ||||
|  | ||||
|   // The offset of the point in the compilation unit header immediately | ||||
|   // after the initial length field. | ||||
|   uint64_t post_length_offset_; | ||||
|  | ||||
|   // The length of the compilation unit, not including the initial length field. | ||||
|   Label length_; | ||||
| }; | ||||
|  | ||||
| // A subclass of test_assembler::Section specialized for constructing DWARF | ||||
| // abbreviation tables. | ||||
| class TestAbbrevTable: public google_breakpad::test_assembler::Section { | ||||
|  public: | ||||
|   typedef dwarf2reader::DwarfTag DwarfTag; | ||||
|   typedef dwarf2reader::DwarfAttribute DwarfAttribute; | ||||
|   typedef dwarf2reader::DwarfForm DwarfForm; | ||||
|   typedef dwarf2reader::DwarfHasChild DwarfHasChild; | ||||
|   typedef google_breakpad::test_assembler::Label Label; | ||||
|  | ||||
|   // Start a new abbreviation table entry for abbreviation code |code|, | ||||
|   // encoding a DIE whose tag is |tag|, and which has children if and only | ||||
|   // if |has_children| is true. | ||||
|   TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) { | ||||
|     assert(code != 0); | ||||
|     ULEB128(code); | ||||
|     ULEB128(static_cast<unsigned>(tag)); | ||||
|     D8(static_cast<unsigned>(has_children)); | ||||
|     return *this; | ||||
|   }; | ||||
|  | ||||
|   // Add an attribute to the current abbreviation code whose name is |name| | ||||
|   // and whose form is |form|. | ||||
|   TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) { | ||||
|     ULEB128(static_cast<unsigned>(name)); | ||||
|     ULEB128(static_cast<unsigned>(form)); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Finish the current abbreviation code. | ||||
|   TestAbbrevTable &EndAbbrev() { | ||||
|     ULEB128(0); | ||||
|     ULEB128(0); | ||||
|     return *this; | ||||
|   } | ||||
|  | ||||
|   // Finish the current abbreviation table. | ||||
|   TestAbbrevTable &EndTable() { | ||||
|     ULEB128(0); | ||||
|     return *this; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| #endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ | ||||
							
								
								
									
										231
									
								
								sdk/breakpad/common/dwarf/functioninfo.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								sdk/breakpad/common/dwarf/functioninfo.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // This is a client for the dwarf2reader to extract function and line | ||||
| // information from the debug info. | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <limits.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| #include <map> | ||||
| #include <queue> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/dwarf/functioninfo.h" | ||||
| #include "common/dwarf/bytereader.h" | ||||
| #include "common/scoped_ptr.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using google_breakpad::scoped_ptr; | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| CULineInfoHandler::CULineInfoHandler(std::vector<SourceFileInfo>* files, | ||||
|                                      std::vector<string>* dirs, | ||||
|                                      LineMap* linemap):linemap_(linemap), | ||||
|                                                        files_(files), | ||||
|                                                        dirs_(dirs) { | ||||
|   // The dirs and files are 1 indexed, so just make sure we put | ||||
|   // nothing in the 0 vector. | ||||
|   assert(dirs->size() == 0); | ||||
|   assert(files->size() == 0); | ||||
|   dirs->push_back(""); | ||||
|   SourceFileInfo s; | ||||
|   s.name = ""; | ||||
|   s.lowpc = ULLONG_MAX; | ||||
|   files->push_back(s); | ||||
| } | ||||
|  | ||||
| void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) { | ||||
|   // These should never come out of order, actually | ||||
|   assert(dir_num == dirs_->size()); | ||||
|   dirs_->push_back(name); | ||||
| } | ||||
|  | ||||
| void CULineInfoHandler::DefineFile(const string& name, | ||||
|                                    int32 file_num, uint32 dir_num, | ||||
|                                    uint64 mod_time, uint64 length) { | ||||
|   assert(dir_num >= 0); | ||||
|   assert(dir_num < dirs_->size()); | ||||
|  | ||||
|   // These should never come out of order, actually. | ||||
|   if (file_num == (int32)files_->size() || file_num == -1) { | ||||
|     string dir = dirs_->at(dir_num); | ||||
|  | ||||
|     SourceFileInfo s; | ||||
|     s.lowpc = ULLONG_MAX; | ||||
|  | ||||
|     if (dir == "") { | ||||
|       s.name = name; | ||||
|     } else { | ||||
|       s.name = dir + "/" + name; | ||||
|     } | ||||
|  | ||||
|     files_->push_back(s); | ||||
|   } else { | ||||
|     fprintf(stderr, "error in DefineFile"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num, | ||||
|                                 uint32 line_num, uint32 column_num) { | ||||
|   if (file_num < files_->size()) { | ||||
|     linemap_->insert( | ||||
|         std::make_pair(address, | ||||
|                        std::make_pair(files_->at(file_num).name.c_str(), | ||||
|                                       line_num))); | ||||
|  | ||||
|     if (address < files_->at(file_num).lowpc) { | ||||
|       files_->at(file_num).lowpc = address; | ||||
|     } | ||||
|   } else { | ||||
|     fprintf(stderr, "error in AddLine"); | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset, | ||||
|                                                  uint8 address_size, | ||||
|                                                  uint8 offset_size, | ||||
|                                                  uint64 cu_length, | ||||
|                                                  uint8 dwarf_version) { | ||||
|   current_compilation_unit_offset_ = offset; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
|  | ||||
| // For function info, we only care about subprograms and inlined | ||||
| // subroutines. For line info, the DW_AT_stmt_list lives in the | ||||
| // compile unit tag. | ||||
|  | ||||
| bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag) { | ||||
|   switch (tag) { | ||||
|     case DW_TAG_subprogram: | ||||
|     case DW_TAG_inlined_subroutine: { | ||||
|       current_function_info_ = new FunctionInfo; | ||||
|       current_function_info_->lowpc = current_function_info_->highpc = 0; | ||||
|       current_function_info_->name = ""; | ||||
|       current_function_info_->line = 0; | ||||
|       current_function_info_->file = ""; | ||||
|       offset_to_funcinfo_->insert(std::make_pair(offset, | ||||
|                                                  current_function_info_)); | ||||
|     }; | ||||
|       // FALLTHROUGH | ||||
|     case DW_TAG_compile_unit: | ||||
|       return true; | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| // Only care about the name attribute for functions | ||||
|  | ||||
| void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, | ||||
|                                                    enum DwarfAttribute attr, | ||||
|                                                    enum DwarfForm form, | ||||
|                                                    const string &data) { | ||||
|   if (current_function_info_) { | ||||
|     if (attr == DW_AT_name) | ||||
|       current_function_info_->name = data; | ||||
|     else if (attr == DW_AT_MIPS_linkage_name) | ||||
|       current_function_info_->mangled_name = data; | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, | ||||
|                                                      enum DwarfAttribute attr, | ||||
|                                                      enum DwarfForm form, | ||||
|                                                      uint64 data) { | ||||
|   if (attr == DW_AT_stmt_list) { | ||||
|     SectionMap::const_iterator iter = sections_.find("__debug_line"); | ||||
|     assert(iter != sections_.end()); | ||||
|  | ||||
|     scoped_ptr<LineInfo> lireader(new LineInfo(iter->second.first + data, | ||||
|                                                iter->second.second  - data, | ||||
|                                                reader_, linehandler_)); | ||||
|     lireader->Start(); | ||||
|   } else if (current_function_info_) { | ||||
|     switch (attr) { | ||||
|       case DW_AT_low_pc: | ||||
|         current_function_info_->lowpc = data; | ||||
|         break; | ||||
|       case DW_AT_high_pc: | ||||
|         current_function_info_->highpc = data; | ||||
|         break; | ||||
|       case DW_AT_decl_line: | ||||
|         current_function_info_->line = data; | ||||
|         break; | ||||
|       case DW_AT_decl_file: | ||||
|         current_function_info_->file = files_->at(data).name; | ||||
|         break; | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset, | ||||
|                                                       enum DwarfAttribute attr, | ||||
|                                                       enum DwarfForm form, | ||||
|                                                       uint64 data) { | ||||
|   if (current_function_info_) { | ||||
|     switch (attr) { | ||||
|       case DW_AT_specification: { | ||||
|         // Some functions have a "specification" attribute | ||||
|         // which means they were defined elsewhere. The name | ||||
|         // attribute is not repeated, and must be taken from | ||||
|         // the specification DIE. Here we'll assume that | ||||
|         // any DIE referenced in this manner will already have | ||||
|         // been seen, but that's not really required by the spec. | ||||
|         FunctionMap::iterator iter = offset_to_funcinfo_->find(data); | ||||
|         if (iter != offset_to_funcinfo_->end()) { | ||||
|           current_function_info_->name = iter->second->name; | ||||
|           current_function_info_->mangled_name = iter->second->mangled_name; | ||||
|         } else { | ||||
|           // If you hit this, this code probably needs to be rewritten. | ||||
|           fprintf(stderr, | ||||
|                   "Error: DW_AT_specification was seen before the referenced " | ||||
|                   "DIE! (Looking for DIE at offset %08llx, in DIE at " | ||||
|                   "offset %08llx)\n", data, offset); | ||||
|         } | ||||
|         break; | ||||
|       } | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| void CUFunctionInfoHandler::EndDIE(uint64 offset) { | ||||
|   if (current_function_info_ && current_function_info_->lowpc) | ||||
|     address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc, | ||||
|                                                 current_function_info_)); | ||||
| } | ||||
|  | ||||
| }  // namespace dwarf2reader | ||||
							
								
								
									
										188
									
								
								sdk/breakpad/common/dwarf/functioninfo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								sdk/breakpad/common/dwarf/functioninfo.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| // Copyright (c) 2010 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
|  | ||||
| // This file contains the definitions for a DWARF2/3 information | ||||
| // collector that uses the DWARF2/3 reader interface to build a mapping | ||||
| // of addresses to files, lines, and functions. | ||||
|  | ||||
| #ifndef COMMON_DWARF_FUNCTIONINFO_H__ | ||||
| #define COMMON_DWARF_FUNCTIONINFO_H__ | ||||
|  | ||||
| #include <map> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/dwarf/dwarf2reader.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| struct FunctionInfo { | ||||
|   // Name of the function | ||||
|   string name; | ||||
|   // Mangled name of the function | ||||
|   string mangled_name; | ||||
|   // File containing this function | ||||
|   string file; | ||||
|   // Line number for start of function. | ||||
|   uint32 line; | ||||
|   // Beginning address for this function | ||||
|   uint64 lowpc; | ||||
|   // End address for this function. | ||||
|   uint64 highpc; | ||||
| }; | ||||
|  | ||||
| struct SourceFileInfo { | ||||
|   // Name of the source file name | ||||
|   string name; | ||||
|   // Low address of source file name | ||||
|   uint64 lowpc; | ||||
| }; | ||||
|  | ||||
| typedef std::map<uint64, FunctionInfo*> FunctionMap; | ||||
| typedef std::map<uint64, std::pair<string, uint32> > LineMap; | ||||
|  | ||||
| // This class is a basic line info handler that fills in the dirs, | ||||
| // file, and linemap passed into it with the data produced from the | ||||
| // LineInfoHandler. | ||||
| class CULineInfoHandler: public LineInfoHandler { | ||||
|  public: | ||||
|  | ||||
|   // | ||||
|   CULineInfoHandler(std::vector<SourceFileInfo>* files, | ||||
|                     std::vector<string>* dirs, | ||||
|                     LineMap* linemap); | ||||
|   virtual ~CULineInfoHandler() { } | ||||
|  | ||||
|   // Called when we define a directory.  We just place NAME into dirs_ | ||||
|   // at position DIR_NUM. | ||||
|   virtual void DefineDir(const string& name, uint32 dir_num); | ||||
|  | ||||
|   // Called when we define a filename.  We just place | ||||
|   // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM. | ||||
|   virtual void DefineFile(const string& name, int32 file_num, | ||||
|                           uint32 dir_num, uint64 mod_time, uint64 length); | ||||
|  | ||||
|  | ||||
|   // Called when the line info reader has a new line, address pair | ||||
|   // ready for us. ADDRESS is the address of the code, LENGTH is the | ||||
|   // length of its machine code in bytes, FILE_NUM is the file number | ||||
|   // containing the code, LINE_NUM is the line number in that file for | ||||
|   // the code, and COLUMN_NUM is the column number the code starts at, | ||||
|   // if we know it (0 otherwise). | ||||
|   virtual void AddLine(uint64 address, uint64 length, | ||||
|                        uint32 file_num, uint32 line_num, uint32 column_num); | ||||
|  | ||||
|  private: | ||||
|   LineMap* linemap_; | ||||
|   std::vector<SourceFileInfo>* files_; | ||||
|   std::vector<string>* dirs_; | ||||
| }; | ||||
|  | ||||
| class CUFunctionInfoHandler: public Dwarf2Handler { | ||||
|  public: | ||||
|   CUFunctionInfoHandler(std::vector<SourceFileInfo>* files, | ||||
|                         std::vector<string>* dirs, | ||||
|                         LineMap* linemap, | ||||
|                         FunctionMap* offset_to_funcinfo, | ||||
|                         FunctionMap* address_to_funcinfo, | ||||
|                         CULineInfoHandler* linehandler, | ||||
|                         const SectionMap& sections, | ||||
|                         ByteReader* reader) | ||||
|       : files_(files), dirs_(dirs), linemap_(linemap), | ||||
|         offset_to_funcinfo_(offset_to_funcinfo), | ||||
|         address_to_funcinfo_(address_to_funcinfo), | ||||
|         linehandler_(linehandler), sections_(sections), | ||||
|         reader_(reader), current_function_info_(NULL) { } | ||||
|  | ||||
|   virtual ~CUFunctionInfoHandler() { } | ||||
|  | ||||
|   // Start to process a compilation unit at OFFSET from the beginning of the | ||||
|   // .debug_info section.  We want to see all compilation units, so we | ||||
|   // always return true. | ||||
|  | ||||
|   virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||
|                                     uint8 offset_size, uint64 cu_length, | ||||
|                                     uint8 dwarf_version); | ||||
|  | ||||
|   // Start to process a DIE at OFFSET from the beginning of the | ||||
|   // .debug_info section.  We only care about function related DIE's. | ||||
|   virtual bool StartDIE(uint64 offset, enum DwarfTag tag); | ||||
|  | ||||
|   // Called when we have an attribute with unsigned data to give to | ||||
|   // our handler.  The attribute is for the DIE at OFFSET from the | ||||
|   // beginning of the .debug_info section, has a name of ATTR, a form of | ||||
|   // FORM, and the actual data of the attribute is in DATA. | ||||
|   virtual void ProcessAttributeUnsigned(uint64 offset, | ||||
|                                         enum DwarfAttribute attr, | ||||
|                                         enum DwarfForm form, | ||||
|                                         uint64 data); | ||||
|  | ||||
|   // Called when we have an attribute with a DIE reference to give to | ||||
|   // our handler.  The attribute is for the DIE at OFFSET from the | ||||
|   // beginning of the .debug_info section, has a name of ATTR, a form of | ||||
|   // FORM, and the offset of the referenced DIE from the start of the | ||||
|   // .debug_info section is in DATA. | ||||
|   virtual void ProcessAttributeReference(uint64 offset, | ||||
|                                          enum DwarfAttribute attr, | ||||
|                                          enum DwarfForm form, | ||||
|                                          uint64 data); | ||||
|  | ||||
|   // Called when we have an attribute with string data to give to | ||||
|   // our handler.  The attribute is for the DIE at OFFSET from the | ||||
|   // beginning of the .debug_info section, has a name of ATTR, a form of | ||||
|   // FORM, and the actual data of the attribute is in DATA. | ||||
|   virtual void ProcessAttributeString(uint64 offset, | ||||
|                                       enum DwarfAttribute attr, | ||||
|                                       enum DwarfForm form, | ||||
|                                       const string& data); | ||||
|  | ||||
|   // Called when finished processing the DIE at OFFSET. | ||||
|   // Because DWARF2/3 specifies a tree of DIEs, you may get starts | ||||
|   // before ends of the previous DIE, as we process children before | ||||
|   // ending the parent. | ||||
|   virtual void EndDIE(uint64 offset); | ||||
|  | ||||
|  private: | ||||
|   std::vector<SourceFileInfo>* files_; | ||||
|   std::vector<string>* dirs_; | ||||
|   LineMap* linemap_; | ||||
|   FunctionMap* offset_to_funcinfo_; | ||||
|   FunctionMap* address_to_funcinfo_; | ||||
|   CULineInfoHandler* linehandler_; | ||||
|   const SectionMap& sections_; | ||||
|   ByteReader* reader_; | ||||
|   FunctionInfo* current_function_info_; | ||||
|   uint64 current_compilation_unit_offset_; | ||||
| }; | ||||
|  | ||||
| }  // namespace dwarf2reader | ||||
| #endif  // COMMON_DWARF_FUNCTIONINFO_H__ | ||||
							
								
								
									
										61
									
								
								sdk/breakpad/common/dwarf/line_state_machine.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								sdk/breakpad/common/dwarf/line_state_machine.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| // Copyright 2008 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
|  | ||||
| #ifndef COMMON_DWARF_LINE_STATE_MACHINE_H__ | ||||
| #define COMMON_DWARF_LINE_STATE_MACHINE_H__ | ||||
|  | ||||
| namespace dwarf2reader { | ||||
|  | ||||
| // This is the format of a DWARF2/3 line state machine that we process | ||||
| // opcodes using.  There is no need for anything outside the lineinfo | ||||
| // processor to know how this works. | ||||
| struct LineStateMachine { | ||||
|   void Reset(bool default_is_stmt) { | ||||
|     file_num = 1; | ||||
|     address = 0; | ||||
|     line_num = 1; | ||||
|     column_num = 0; | ||||
|     is_stmt = default_is_stmt; | ||||
|     basic_block = false; | ||||
|     end_sequence = false; | ||||
|   } | ||||
|  | ||||
|   uint32 file_num; | ||||
|   uint64 address; | ||||
|   uint32 line_num; | ||||
|   uint32 column_num; | ||||
|   bool is_stmt;  // stmt means statement. | ||||
|   bool basic_block; | ||||
|   bool end_sequence; | ||||
| }; | ||||
|  | ||||
| }  // namespace dwarf2reader | ||||
|  | ||||
|  | ||||
| #endif  // COMMON_DWARF_LINE_STATE_MACHINE_H__ | ||||
							
								
								
									
										55
									
								
								sdk/breakpad/common/dwarf/types.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								sdk/breakpad/common/dwarf/types.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,55 @@ | ||||
| // Copyright 2008 Google, Inc.  All Rights reserved | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
|  | ||||
| // This file contains some typedefs for basic types | ||||
|  | ||||
|  | ||||
| #ifndef _COMMON_DWARF_TYPES_H__ | ||||
| #define _COMMON_DWARF_TYPES_H__ | ||||
|  | ||||
| #include <stdint.h> | ||||
|  | ||||
| typedef signed char         int8; | ||||
| typedef short               int16; | ||||
| typedef int                 int32; | ||||
| typedef long long           int64; | ||||
|  | ||||
| typedef unsigned char      uint8; | ||||
| typedef unsigned short     uint16; | ||||
| typedef unsigned int       uint32; | ||||
| typedef unsigned long long uint64; | ||||
|  | ||||
| #ifdef __PTRDIFF_TYPE__ | ||||
| typedef          __PTRDIFF_TYPE__ intptr; | ||||
| typedef unsigned __PTRDIFF_TYPE__ uintptr; | ||||
| #else | ||||
| #error "Can't find pointer-sized integral types." | ||||
| #endif | ||||
|  | ||||
| #endif // _COMMON_DWARF_TYPES_H__ | ||||
							
								
								
									
										275
									
								
								sdk/breakpad/common/dwarf_cfi_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								sdk/breakpad/common/dwarf_cfi_to_module.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // Implementation of google_breakpad::DwarfCFIToModule. | ||||
| // See dwarf_cfi_to_module.h for details. | ||||
|  | ||||
| #include <sstream> | ||||
|  | ||||
| #include "common/dwarf_cfi_to_module.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| using std::ostringstream; | ||||
|  | ||||
| vector<string> DwarfCFIToModule::RegisterNames::MakeVector( | ||||
|     const char * const *strings, | ||||
|     size_t size) { | ||||
|   vector<string> names(strings, strings + size); | ||||
|   return names; | ||||
| } | ||||
|  | ||||
| vector<string> DwarfCFIToModule::RegisterNames::I386() { | ||||
|   static const char *const names[] = { | ||||
|     "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", | ||||
|     "$eip", "$eflags", "$unused1", | ||||
|     "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", | ||||
|     "$unused2", "$unused3", | ||||
|     "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", | ||||
|     "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", | ||||
|     "$fcw", "$fsw", "$mxcsr", | ||||
|     "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", | ||||
|     "$tr", "$ldtr" | ||||
|   }; | ||||
|  | ||||
|   return MakeVector(names, sizeof(names) / sizeof(names[0])); | ||||
| } | ||||
|  | ||||
| vector<string> DwarfCFIToModule::RegisterNames::X86_64() { | ||||
|   static const char *const names[] = { | ||||
|     "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", | ||||
|     "$r8",  "$r9",  "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", | ||||
|     "$rip", | ||||
|     "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", | ||||
|     "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", | ||||
|     "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", | ||||
|     "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", | ||||
|     "$rflags", | ||||
|     "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", | ||||
|     "$fs.base", "$gs.base", "$unused3", "$unused4", | ||||
|     "$tr", "$ldtr", | ||||
|     "$mxcsr", "$fcw", "$fsw" | ||||
|   }; | ||||
|  | ||||
|   return MakeVector(names, sizeof(names) / sizeof(names[0])); | ||||
| } | ||||
|  | ||||
| // Per ARM IHI 0040A, section 3.1 | ||||
| vector<string> DwarfCFIToModule::RegisterNames::ARM() { | ||||
|   static const char *const names[] = { | ||||
|     "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7", | ||||
|     "r8",  "r9",  "r10", "r11", "r12", "sp",  "lr",  "pc", | ||||
|     "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7", | ||||
|     "fps", "cpsr", "",   "",    "",    "",    "",    "", | ||||
|     "",    "",    "",    "",    "",    "",    "",    "", | ||||
|     "",    "",    "",    "",    "",    "",    "",    "", | ||||
|     "",    "",    "",    "",    "",    "",    "",    "", | ||||
|     "",    "",    "",    "",    "",    "",    "",    "", | ||||
|     "s0",  "s1",  "s2",  "s3",  "s4",  "s5",  "s6",  "s7", | ||||
|     "s8",  "s9",  "s10", "s11", "s12", "s13", "s14", "s15", | ||||
|     "s16", "s17", "s18", "s19", "s20", "s21", "s22", "s23", | ||||
|     "s24", "s25", "s26", "s27", "s28", "s29", "s30", "s31", | ||||
|     "f0",  "f1",  "f2",  "f3",  "f4",  "f5",  "f6",  "f7" | ||||
|   }; | ||||
|  | ||||
|   return MakeVector(names, sizeof(names) / sizeof(names[0])); | ||||
| } | ||||
|  | ||||
| vector<string> DwarfCFIToModule::RegisterNames::MIPS() { | ||||
|   static const char* const kRegisterNames[] = { | ||||
|     "$zero", "$at",  "$v0",  "$v1",  "$a0",   "$a1",  "$a2",  "$a3", | ||||
|     "$t0",   "$t1",  "$t2",  "$t3",  "$t4",   "$t5",  "$t6",  "$t7", | ||||
|     "$s0",   "$s1",  "$s2",  "$s3",  "$s4",   "$s5",  "$s6",  "$s7", | ||||
|     "$t8",   "$t9",  "$k0",  "$k1",  "$gp",   "$sp",  "$fp",  "$ra", | ||||
|     "$lo",   "$hi",  "$pc",  "$f0",  "$f2",   "$f3",  "$f4",  "$f5", | ||||
|     "$f6",   "$f7",  "$f8",  "$f9",  "$f10",  "$f11", "$f12", "$f13", | ||||
|     "$f14",  "$f15", "$f16", "$f17", "$f18",  "$f19", "$f20", | ||||
|     "$f21",  "$f22", "$f23", "$f24", "$f25",  "$f26", "$f27", | ||||
|     "$f28",  "$f29", "$f30", "$f31", "$fcsr", "$fir" | ||||
|   }; | ||||
|  | ||||
|   return MakeVector(kRegisterNames,  | ||||
|                     sizeof(kRegisterNames) / sizeof(kRegisterNames[0])); | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length, | ||||
|                              uint8 version, const string &augmentation, | ||||
|                              unsigned return_address) { | ||||
|   assert(!entry_); | ||||
|  | ||||
|   // If dwarf2reader::CallFrameInfo can handle this version and | ||||
|   // augmentation, then we should be okay with that, so there's no | ||||
|   // need to check them here. | ||||
|  | ||||
|   // Get ready to collect entries. | ||||
|   entry_ = new Module::StackFrameEntry; | ||||
|   entry_->address = address; | ||||
|   entry_->size = length; | ||||
|   entry_offset_ = offset; | ||||
|   return_address_ = return_address; | ||||
|  | ||||
|   // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI | ||||
|   // may not establish any rule for .ra if the return address column | ||||
|   // is an ordinary register, and that register holds the return | ||||
|   // address on entry to the function. So establish an initial .ra | ||||
|   // rule citing the return address register. | ||||
|   if (return_address_ < register_names_.size()) | ||||
|     entry_->initial_rules[ra_name_] = register_names_[return_address_]; | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| string DwarfCFIToModule::RegisterName(int i) { | ||||
|   assert(entry_); | ||||
|   if (i < 0) { | ||||
|     assert(i == kCFARegister); | ||||
|     return cfa_name_; | ||||
|   } | ||||
|   unsigned reg = i; | ||||
|   if (reg == return_address_) | ||||
|     return ra_name_; | ||||
|  | ||||
|   // Ensure that a non-empty name exists for this register value. | ||||
|   if (reg < register_names_.size() && !register_names_[reg].empty()) | ||||
|     return register_names_[reg]; | ||||
|  | ||||
|   reporter_->UnnamedRegister(entry_offset_, reg); | ||||
|   char buf[30]; | ||||
|   sprintf(buf, "unnamed_register%u", reg); | ||||
|   return buf; | ||||
| } | ||||
|  | ||||
| void DwarfCFIToModule::Record(Module::Address address, int reg, | ||||
|                               const string &rule) { | ||||
|   assert(entry_); | ||||
|  | ||||
|   // Place the name in our global set of strings, and then use the string | ||||
|   // from the set. Even though the assignment looks like a copy, all the | ||||
|   // major std::string implementations use reference counting internally, | ||||
|   // so the effect is to have all our data structures share copies of rules | ||||
|   // whenever possible. Since register names are drawn from a | ||||
|   // vector<string>, register names are already shared. | ||||
|   string shared_rule = *common_strings_.insert(rule).first; | ||||
|  | ||||
|   // Is this one of this entry's initial rules? | ||||
|   if (address == entry_->address) | ||||
|     entry_->initial_rules[RegisterName(reg)] = shared_rule; | ||||
|   // File it under the appropriate address. | ||||
|   else | ||||
|     entry_->rule_changes[address][RegisterName(reg)] = shared_rule; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) { | ||||
|   reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); | ||||
|   // Treat this as a non-fatal error. | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) { | ||||
|   ostringstream s; | ||||
|   s << RegisterName(reg); | ||||
|   Record(address, reg, s.str()); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::OffsetRule(uint64 address, int reg, | ||||
|                                   int base_register, long offset) { | ||||
|   ostringstream s; | ||||
|   s << RegisterName(base_register) << " " << offset << " + ^"; | ||||
|   Record(address, reg, s.str()); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg, | ||||
|                                      int base_register, long offset) { | ||||
|   ostringstream s; | ||||
|   s << RegisterName(base_register) << " " << offset << " +"; | ||||
|   Record(address, reg, s.str()); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::RegisterRule(uint64 address, int reg, | ||||
|                                     int base_register) { | ||||
|   ostringstream s; | ||||
|   s << RegisterName(base_register); | ||||
|   Record(address, reg, s.str()); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg, | ||||
|                                       const string &expression) { | ||||
|   reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); | ||||
|   // Treat this as a non-fatal error. | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg, | ||||
|                                          const string &expression) { | ||||
|   reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); | ||||
|   // Treat this as a non-fatal error. | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool DwarfCFIToModule::End() { | ||||
|   module_->AddStackFrameEntry(entry_); | ||||
|   entry_ = NULL; | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) { | ||||
|   fprintf(stderr, "%s, section '%s': " | ||||
|           "the call frame entry at offset 0x%zx refers to register %d," | ||||
|           " whose name we don't know\n", | ||||
|           file_.c_str(), section_.c_str(), offset, reg); | ||||
| } | ||||
|  | ||||
| void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset, | ||||
|                                                        const string ®) { | ||||
|   fprintf(stderr, "%s, section '%s': " | ||||
|           "the call frame entry at offset 0x%zx sets the rule for " | ||||
|           "register '%s' to 'undefined', but the Breakpad symbol file format" | ||||
|           " cannot express this\n", | ||||
|           file_.c_str(), section_.c_str(), offset, reg.c_str()); | ||||
| } | ||||
|  | ||||
| void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset, | ||||
|                                                          const string ®) { | ||||
|   fprintf(stderr, "%s, section '%s': " | ||||
|           "the call frame entry at offset 0x%zx uses a DWARF expression to" | ||||
|           " describe how to recover register '%s', " | ||||
|           " but this translator cannot yet translate DWARF expressions to" | ||||
|           " Breakpad postfix expressions\n", | ||||
|           file_.c_str(), section_.c_str(), offset, reg.c_str()); | ||||
| } | ||||
|  | ||||
| } // namespace google_breakpad | ||||
							
								
								
									
										199
									
								
								sdk/breakpad/common/dwarf_cfi_to_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								sdk/breakpad/common/dwarf_cfi_to_module.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which | ||||
| // accepts parsed DWARF call frame info and adds it to a | ||||
| // google_breakpad::Module object, which can write that information to | ||||
| // a Breakpad symbol file. | ||||
|  | ||||
| #ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H | ||||
| #define COMMON_LINUX_DWARF_CFI_TO_MODULE_H | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| #include <set> | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/module.h" | ||||
| #include "common/dwarf/dwarf2reader.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| using dwarf2reader::CallFrameInfo; | ||||
| using google_breakpad::Module; | ||||
| using std::set; | ||||
| using std::vector; | ||||
|  | ||||
| // A class that accepts parsed call frame information from the DWARF | ||||
| // CFI parser and populates a google_breakpad::Module object with the | ||||
| // contents. | ||||
| class DwarfCFIToModule: public CallFrameInfo::Handler { | ||||
|  public: | ||||
|  | ||||
|   // DwarfCFIToModule uses an instance of this class to report errors | ||||
|   // detected while converting DWARF CFI to Breakpad STACK CFI records. | ||||
|   class Reporter { | ||||
|    public: | ||||
|     // Create a reporter that writes messages to the standard error | ||||
|     // stream. FILE is the name of the file we're processing, and | ||||
|     // SECTION is the name of the section within that file that we're | ||||
|     // looking at (.debug_frame, .eh_frame, etc.). | ||||
|     Reporter(const string &file, const string §ion) | ||||
|       : file_(file), section_(section) { } | ||||
|     virtual ~Reporter() { } | ||||
|  | ||||
|     // The DWARF CFI entry at OFFSET cites register REG, but REG is not | ||||
|     // covered by the vector of register names passed to the | ||||
|     // DwarfCFIToModule constructor, nor does it match the return | ||||
|     // address column number for this entry. | ||||
|     virtual void UnnamedRegister(size_t offset, int reg); | ||||
|  | ||||
|     // The DWARF CFI entry at OFFSET says that REG is undefined, but the | ||||
|     // Breakpad symbol file format cannot express this. | ||||
|     virtual void UndefinedNotSupported(size_t offset, const string ®); | ||||
|  | ||||
|     // The DWARF CFI entry at OFFSET says that REG uses a DWARF | ||||
|     // expression to find its value, but DwarfCFIToModule is not | ||||
|     // capable of translating DWARF expressions to Breakpad postfix | ||||
|     // expressions. | ||||
|     virtual void ExpressionsNotSupported(size_t offset, const string ®); | ||||
|  | ||||
|   protected: | ||||
|     string file_, section_; | ||||
|   }; | ||||
|  | ||||
|   // Register name tables. If TABLE is a vector returned by one of these | ||||
|   // functions, then TABLE[R] is the name of the register numbered R in | ||||
|   // DWARF call frame information. | ||||
|   class RegisterNames { | ||||
|    public: | ||||
|     // Intel's "x86" or IA-32. | ||||
|     static vector<string> I386(); | ||||
|  | ||||
|     // AMD x86_64, AMD64, Intel EM64T, or Intel 64 | ||||
|     static vector<string> X86_64(); | ||||
|  | ||||
|     // ARM. | ||||
|     static vector<string> ARM(); | ||||
|      | ||||
|     // MIPS. | ||||
|     static vector<string> MIPS(); | ||||
|  | ||||
|    private: | ||||
|     // Given STRINGS, an array of C strings with SIZE elements, return an | ||||
|     // equivalent vector<string>. | ||||
|     static vector<string> MakeVector(const char * const *strings, size_t size); | ||||
|   }; | ||||
|  | ||||
|   // Create a handler for the dwarf2reader::CallFrameInfo parser that | ||||
|   // records the stack unwinding information it receives in MODULE. | ||||
|   // | ||||
|   // Use REGISTER_NAMES[I] as the name of register number I; *this | ||||
|   // keeps a reference to the vector, so the vector should remain | ||||
|   // alive for as long as the DwarfCFIToModule does. | ||||
|   // | ||||
|   // Use REPORTER for reporting problems encountered in the conversion | ||||
|   // process. | ||||
|   DwarfCFIToModule(Module *module, const vector<string> ®ister_names, | ||||
|                    Reporter *reporter) | ||||
|       : module_(module), register_names_(register_names), reporter_(reporter), | ||||
|         entry_(NULL), return_address_(-1), cfa_name_(".cfa"), ra_name_(".ra") { | ||||
|   } | ||||
|   virtual ~DwarfCFIToModule() { delete entry_; } | ||||
|  | ||||
|   virtual bool Entry(size_t offset, uint64 address, uint64 length, | ||||
|                      uint8 version, const string &augmentation, | ||||
|                      unsigned return_address); | ||||
|   virtual bool UndefinedRule(uint64 address, int reg); | ||||
|   virtual bool SameValueRule(uint64 address, int reg); | ||||
|   virtual bool OffsetRule(uint64 address, int reg, | ||||
|                           int base_register, long offset); | ||||
|   virtual bool ValOffsetRule(uint64 address, int reg, | ||||
|                              int base_register, long offset); | ||||
|   virtual bool RegisterRule(uint64 address, int reg, int base_register); | ||||
|   virtual bool ExpressionRule(uint64 address, int reg, | ||||
|                               const string &expression); | ||||
|   virtual bool ValExpressionRule(uint64 address, int reg, | ||||
|                                  const string &expression); | ||||
|   virtual bool End(); | ||||
|  | ||||
|  private: | ||||
|   // Return the name to use for register REG. | ||||
|   string RegisterName(int i); | ||||
|  | ||||
|   // Record RULE for register REG at ADDRESS. | ||||
|   void Record(Module::Address address, int reg, const string &rule); | ||||
|  | ||||
|   // The module to which we should add entries. | ||||
|   Module *module_; | ||||
|  | ||||
|   // Map from register numbers to register names. | ||||
|   const vector<string> ®ister_names_; | ||||
|  | ||||
|   // The reporter to use to report problems. | ||||
|   Reporter *reporter_; | ||||
|  | ||||
|   // The current entry we're constructing. | ||||
|   Module::StackFrameEntry *entry_; | ||||
|  | ||||
|   // The section offset of the current frame description entry, for | ||||
|   // use in error messages. | ||||
|   size_t entry_offset_; | ||||
|  | ||||
|   // The return address column for that entry. | ||||
|   unsigned return_address_; | ||||
|  | ||||
|   // The names of the return address and canonical frame address. Putting | ||||
|   // these here instead of using string literals allows us to share their | ||||
|   // texts in reference-counted std::string implementations (all the | ||||
|   // popular ones). Many, many rules cite these strings. | ||||
|   string cfa_name_, ra_name_; | ||||
|  | ||||
|   // A set of strings used by this CFI. Before storing a string in one of | ||||
|   // our data structures, insert it into this set, and then use the string | ||||
|   // from the set. | ||||
|   //  | ||||
|   // Because std::string uses reference counting internally, simply using | ||||
|   // strings from this set, even if passed by value, assigned, or held | ||||
|   // directly in structures and containers (map<string, ...>, for example), | ||||
|   // causes those strings to share a single instance of each distinct piece | ||||
|   // of text. | ||||
|   set<string> common_strings_; | ||||
| }; | ||||
|  | ||||
| } // namespace google_breakpad | ||||
|  | ||||
| #endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H | ||||
							
								
								
									
										306
									
								
								sdk/breakpad/common/dwarf_cfi_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								sdk/breakpad/common/dwarf_cfi_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,306 @@ | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule. | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/dwarf_cfi_to_module.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using std::vector; | ||||
|  | ||||
| using google_breakpad::Module; | ||||
| using google_breakpad::DwarfCFIToModule; | ||||
| using testing::ContainerEq; | ||||
| using testing::Test; | ||||
| using testing::_; | ||||
|  | ||||
| struct MockCFIReporter: public DwarfCFIToModule::Reporter { | ||||
|   MockCFIReporter(const string &file, const string §ion) | ||||
|       : Reporter(file, section) { } | ||||
|   MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg)); | ||||
|   MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string ®)); | ||||
|   MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string ®)); | ||||
| }; | ||||
|  | ||||
| struct DwarfCFIToModuleFixture { | ||||
|   DwarfCFIToModuleFixture() | ||||
|       : module("module name", "module os", "module arch", "module id"), | ||||
|         reporter("reporter file", "reporter section"), | ||||
|         handler(&module, register_names, &reporter) { | ||||
|     register_names.push_back("reg0"); | ||||
|     register_names.push_back("reg1"); | ||||
|     register_names.push_back("reg2"); | ||||
|     register_names.push_back("reg3"); | ||||
|     register_names.push_back("reg4"); | ||||
|     register_names.push_back("reg5"); | ||||
|     register_names.push_back("reg6"); | ||||
|     register_names.push_back("reg7"); | ||||
|     register_names.push_back("sp"); | ||||
|     register_names.push_back("pc"); | ||||
|     register_names.push_back(""); | ||||
|  | ||||
|     EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0); | ||||
|     EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0); | ||||
|     EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0); | ||||
|   } | ||||
|  | ||||
|   Module module; | ||||
|   vector<string> register_names; | ||||
|   MockCFIReporter reporter; | ||||
|   DwarfCFIToModule handler; | ||||
|   vector<Module::StackFrameEntry *> entries; | ||||
| }; | ||||
|  | ||||
| class Entry: public DwarfCFIToModuleFixture, public Test { }; | ||||
|  | ||||
| TEST_F(Entry, Accept) { | ||||
|   ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL, | ||||
|                             0xb440ce248169c8d6ULL, 3, "", 0xea93c106)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   module.GetStackFrameEntries(&entries); | ||||
|   EXPECT_EQ(1U, entries.size()); | ||||
|   EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address); | ||||
|   EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Entry, AcceptOldVersion) { | ||||
|   ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL, | ||||
|                             0xc771f44958d40bbcULL, 1, "", 0x093c945e)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   module.GetStackFrameEntries(&entries); | ||||
|   EXPECT_EQ(1U, entries.size()); | ||||
|   EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address); | ||||
|   EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| struct RuleFixture: public DwarfCFIToModuleFixture { | ||||
|   RuleFixture() : DwarfCFIToModuleFixture() { | ||||
|     entry_address = 0x89327ebf86b47492ULL; | ||||
|     entry_size    = 0x2f8cd573072fe02aULL; | ||||
|     return_reg    = 0x7886a346; | ||||
|   } | ||||
|   void StartEntry() { | ||||
|     ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size, | ||||
|                               3, "", return_reg)); | ||||
|   } | ||||
|   void CheckEntry() { | ||||
|     module.GetStackFrameEntries(&entries); | ||||
|     EXPECT_EQ(1U, entries.size()); | ||||
|     EXPECT_EQ(entry_address, entries[0]->address); | ||||
|     EXPECT_EQ(entry_size, entries[0]->size); | ||||
|   } | ||||
|   uint64 entry_address, entry_size; | ||||
|   unsigned return_reg; | ||||
| }; | ||||
|  | ||||
| class Rule: public RuleFixture, public Test { }; | ||||
|  | ||||
| TEST_F(Rule, UndefinedRule) { | ||||
|   EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7")); | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.UndefinedRule(entry_address, 7)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, RegisterWithEmptyName) { | ||||
|   EXPECT_CALL(reporter, UnnamedRegister(_, 10)); | ||||
|   EXPECT_CALL(reporter, UndefinedNotSupported(_, "unnamed_register10")); | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.UndefinedRule(entry_address, 10)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, SameValueRule) { | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.SameValueRule(entry_address, 6)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   Module::RuleMap expected_initial; | ||||
|   expected_initial["reg6"] = "reg6"; | ||||
|   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, OffsetRule) { | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg, | ||||
|                                  DwarfCFIToModule::kCFARegister, | ||||
|                                  16927065)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   Module::RuleChangeMap expected_changes; | ||||
|   expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^"; | ||||
|   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, OffsetRuleNegative) { | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.OffsetRule(entry_address + 1, | ||||
|                                  DwarfCFIToModule::kCFARegister, 4, -34530721)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   Module::RuleChangeMap expected_changes; | ||||
|   expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^"; | ||||
|   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, ValOffsetRule) { | ||||
|   // Use an unnamed register number, to exercise that branch of RegisterName. | ||||
|   EXPECT_CALL(reporter, UnnamedRegister(_, 11)); | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7, | ||||
|                                     DwarfCFIToModule::kCFARegister, | ||||
|                                     11, 61812979)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   Module::RuleChangeMap expected_changes; | ||||
|   expected_changes[entry_address + 0x5ab7][".cfa"] = | ||||
|       "unnamed_register11 61812979 +"; | ||||
|   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, RegisterRule) { | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   Module::RuleMap expected_initial; | ||||
|   expected_initial[".ra"] = "reg3"; | ||||
|   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, ExpressionRule) { | ||||
|   EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2")); | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2, | ||||
|                                      "it takes two to tango")); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, ValExpressionRule) { | ||||
|   EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0")); | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0, | ||||
|                                         "bit off more than he could chew")); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   EXPECT_EQ(0U, entries[0]->initial_rules.size()); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, DefaultReturnAddressRule) { | ||||
|   return_reg = 2; | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   Module::RuleMap expected_initial; | ||||
|   expected_initial[".ra"] = "reg2"; | ||||
|   expected_initial["reg0"] = "reg1"; | ||||
|   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, DefaultReturnAddressRuleOverride) { | ||||
|   return_reg = 2; | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   Module::RuleMap expected_initial; | ||||
|   expected_initial[".ra"] = "reg1"; | ||||
|   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||
|   EXPECT_EQ(0U, entries[0]->rule_changes.size()); | ||||
| } | ||||
|  | ||||
| TEST_F(Rule, DefaultReturnAddressRuleLater) { | ||||
|   return_reg = 2; | ||||
|   StartEntry(); | ||||
|   ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1)); | ||||
|   ASSERT_TRUE(handler.End()); | ||||
|   CheckEntry(); | ||||
|   Module::RuleMap expected_initial; | ||||
|   expected_initial[".ra"] = "reg2"; | ||||
|   EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); | ||||
|   Module::RuleChangeMap expected_changes; | ||||
|   expected_changes[entry_address + 1][".ra"] = "reg1"; | ||||
|   EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); | ||||
| } | ||||
|  | ||||
| TEST(RegisterNames, I386) { | ||||
|   vector<string> names = DwarfCFIToModule::RegisterNames::I386(); | ||||
|  | ||||
|   EXPECT_EQ("$eax", names[0]); | ||||
|   EXPECT_EQ("$ecx", names[1]); | ||||
|   EXPECT_EQ("$esp", names[4]); | ||||
|   EXPECT_EQ("$eip", names[8]); | ||||
| } | ||||
|  | ||||
| TEST(RegisterNames, ARM) { | ||||
|   vector<string> names = DwarfCFIToModule::RegisterNames::ARM(); | ||||
|  | ||||
|   EXPECT_EQ("r0", names[0]); | ||||
|   EXPECT_EQ("r10", names[10]); | ||||
|   EXPECT_EQ("sp", names[13]); | ||||
|   EXPECT_EQ("lr", names[14]); | ||||
|   EXPECT_EQ("pc", names[15]); | ||||
| } | ||||
|  | ||||
| TEST(RegisterNames, X86_64) { | ||||
|   vector<string> names = DwarfCFIToModule::RegisterNames::X86_64(); | ||||
|  | ||||
|   EXPECT_EQ("$rax", names[0]); | ||||
|   EXPECT_EQ("$rdx", names[1]); | ||||
|   EXPECT_EQ("$rbp", names[6]); | ||||
|   EXPECT_EQ("$rsp", names[7]); | ||||
|   EXPECT_EQ("$rip", names[16]); | ||||
| } | ||||
							
								
								
									
										1054
									
								
								sdk/breakpad/common/dwarf_cu_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1054
									
								
								sdk/breakpad/common/dwarf_cu_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										315
									
								
								sdk/breakpad/common/dwarf_cu_to_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								sdk/breakpad/common/dwarf_cu_to_module.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,315 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // Add DWARF debugging information to a Breakpad symbol file. This | ||||
| // file defines the DwarfCUToModule class, which accepts parsed DWARF | ||||
| // data and populates a google_breakpad::Module with the results; the | ||||
| // Module can then write its contents as a Breakpad symbol file. | ||||
|  | ||||
| #ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__ | ||||
| #define COMMON_LINUX_DWARF_CU_TO_MODULE_H__ | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/language.h" | ||||
| #include "common/module.h" | ||||
| #include "common/dwarf/bytereader.h" | ||||
| #include "common/dwarf/dwarf2diehandler.h" | ||||
| #include "common/dwarf/dwarf2reader.h" | ||||
| #include "common/scoped_ptr.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| using dwarf2reader::DwarfAttribute; | ||||
| using dwarf2reader::DwarfForm; | ||||
| using dwarf2reader::DwarfLanguage; | ||||
| using dwarf2reader::DwarfTag; | ||||
|  | ||||
| // Populate a google_breakpad::Module with DWARF debugging information. | ||||
| // | ||||
| // An instance of this class can be provided as a handler to a | ||||
| // dwarf2reader::DIEDispatcher, which can in turn be a handler for a | ||||
| // dwarf2reader::CompilationUnit DWARF parser. The handler uses the results | ||||
| // of parsing to populate a google_breakpad::Module with source file, | ||||
| // function, and source line information. | ||||
| class DwarfCUToModule: public dwarf2reader::RootDIEHandler { | ||||
|   struct FilePrivate; | ||||
|  public: | ||||
|   // Information global to the DWARF-bearing file we are processing, | ||||
|   // for use by DwarfCUToModule. Each DwarfCUToModule instance deals | ||||
|   // with a single compilation unit within the file, but information | ||||
|   // global to the whole file is held here. The client is responsible | ||||
|   // for filling it in appropriately (except for the 'file_private' | ||||
|   // field, which the constructor and destructor take care of), and | ||||
|   // then providing it to the DwarfCUToModule instance for each | ||||
|   // compilation unit we process in that file. Set HANDLE_INTER_CU_REFS | ||||
|   // to true to handle debugging symbols with DW_FORM_ref_addr entries. | ||||
|   class FileContext { | ||||
|    public: | ||||
|     FileContext(const string &filename, | ||||
|                 Module *module, | ||||
|                 bool handle_inter_cu_refs); | ||||
|     ~FileContext(); | ||||
|  | ||||
|     // Add CONTENTS of size LENGTH to the section map as NAME. | ||||
|     void AddSectionToSectionMap(const string& name, | ||||
|                                 const char* contents, | ||||
|                                 uint64 length); | ||||
|  | ||||
|     // Clear the section map for testing. | ||||
|     void ClearSectionMapForTest(); | ||||
|  | ||||
|     const dwarf2reader::SectionMap& section_map() const; | ||||
|  | ||||
|    private: | ||||
|     friend class DwarfCUToModule; | ||||
|  | ||||
|     // Clears all the Specifications if HANDLE_INTER_CU_REFS_ is false. | ||||
|     void ClearSpecifications(); | ||||
|  | ||||
|     // Given an OFFSET and a CU that starts at COMPILATION_UNIT_START, returns | ||||
|     // true if this is an inter-compilation unit reference that is not being | ||||
|     // handled. | ||||
|     bool IsUnhandledInterCUReference(uint64 offset, | ||||
|                                      uint64 compilation_unit_start) const; | ||||
|  | ||||
|     // The name of this file, for use in error messages. | ||||
|     const string filename_; | ||||
|  | ||||
|     // A map of this file's sections, used for finding other DWARF | ||||
|     // sections that the .debug_info section may refer to. | ||||
|     dwarf2reader::SectionMap section_map_; | ||||
|  | ||||
|     // The Module to which we're contributing definitions. | ||||
|     Module *module_; | ||||
|  | ||||
|     // True if we are handling references between compilation units. | ||||
|     const bool handle_inter_cu_refs_; | ||||
|  | ||||
|     // Inter-compilation unit data used internally by the handlers. | ||||
|     scoped_ptr<FilePrivate> file_private_; | ||||
|   }; | ||||
|  | ||||
|   // An abstract base class for handlers that handle DWARF line data | ||||
|   // for DwarfCUToModule. DwarfCUToModule could certainly just use | ||||
|   // dwarf2reader::LineInfo itself directly, but decoupling things | ||||
|   // this way makes unit testing a little easier. | ||||
|   class LineToModuleHandler { | ||||
|    public: | ||||
|     LineToModuleHandler() { } | ||||
|     virtual ~LineToModuleHandler() { } | ||||
|  | ||||
|     // Called at the beginning of a new compilation unit, prior to calling | ||||
|     // ReadProgram(). compilation_dir will indicate the path that the | ||||
|     // current compilation unit was compiled in, consistent with the | ||||
|     // DW_AT_comp_dir DIE. | ||||
|     virtual void StartCompilationUnit(const string& compilation_dir) = 0; | ||||
|  | ||||
|     // Populate MODULE and LINES with source file names and code/line | ||||
|     // mappings, given a pointer to some DWARF line number data | ||||
|     // PROGRAM, and an overestimate of its size. Add no zero-length | ||||
|     // lines to LINES. | ||||
|     virtual void ReadProgram(const char *program, uint64 length, | ||||
|                              Module *module, vector<Module::Line> *lines) = 0; | ||||
|   }; | ||||
|  | ||||
|   // The interface DwarfCUToModule uses to report warnings. The member | ||||
|   // function definitions for this class write messages to stderr, but | ||||
|   // you can override them if you'd like to detect or report these | ||||
|   // conditions yourself. | ||||
|   class WarningReporter { | ||||
|    public: | ||||
|     // Warn about problems in the DWARF file FILENAME, in the | ||||
|     // compilation unit at OFFSET. | ||||
|     WarningReporter(const string &filename, uint64 cu_offset) | ||||
|         : filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false), | ||||
|           printed_unpaired_header_(false), | ||||
|           uncovered_warnings_enabled_(false) { } | ||||
|     virtual ~WarningReporter() { } | ||||
|  | ||||
|     // Set the name of the compilation unit we're processing to NAME. | ||||
|     virtual void SetCUName(const string &name) { cu_name_ = name; } | ||||
|  | ||||
|     // Accessor and setter for uncovered_warnings_enabled_. | ||||
|     // UncoveredFunction and UncoveredLine only report a problem if that is | ||||
|     // true. By default, these warnings are disabled, because those | ||||
|     // conditions occur occasionally in healthy code. | ||||
|     virtual bool uncovered_warnings_enabled() const { | ||||
|       return uncovered_warnings_enabled_; | ||||
|     } | ||||
|     virtual void set_uncovered_warnings_enabled(bool value) { | ||||
|       uncovered_warnings_enabled_ = value; | ||||
|     } | ||||
|  | ||||
|     // A DW_AT_specification in the DIE at OFFSET refers to a DIE we | ||||
|     // haven't processed yet, or that wasn't marked as a declaration, | ||||
|     // at TARGET. | ||||
|     virtual void UnknownSpecification(uint64 offset, uint64 target); | ||||
|  | ||||
|     // A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we | ||||
|     // haven't processed yet, or that wasn't marked as inline, at TARGET. | ||||
|     virtual void UnknownAbstractOrigin(uint64 offset, uint64 target); | ||||
|  | ||||
|     // We were unable to find the DWARF section named SECTION_NAME. | ||||
|     virtual void MissingSection(const string §ion_name); | ||||
|  | ||||
|     // The CU's DW_AT_stmt_list offset OFFSET is bogus. | ||||
|     virtual void BadLineInfoOffset(uint64 offset); | ||||
|  | ||||
|     // FUNCTION includes code covered by no line number data. | ||||
|     virtual void UncoveredFunction(const Module::Function &function); | ||||
|  | ||||
|     // Line number NUMBER in LINE_FILE, of length LENGTH, includes code | ||||
|     // covered by no function. | ||||
|     virtual void UncoveredLine(const Module::Line &line); | ||||
|  | ||||
|     // The DW_TAG_subprogram DIE at OFFSET has no name specified directly | ||||
|     // in the DIE, nor via a DW_AT_specification or DW_AT_abstract_origin | ||||
|     // link. | ||||
|     virtual void UnnamedFunction(uint64 offset); | ||||
|  | ||||
|     // The DW_FORM_ref_addr at OFFSET to TARGET was not handled because | ||||
|     // FilePrivate did not retain the inter-CU specification data. | ||||
|     virtual void UnhandledInterCUReference(uint64 offset, uint64 target); | ||||
|  | ||||
|     uint64 cu_offset() const { | ||||
|       return cu_offset_; | ||||
|     } | ||||
|  | ||||
|    protected: | ||||
|     const string filename_; | ||||
|     const uint64 cu_offset_; | ||||
|     string cu_name_; | ||||
|     bool printed_cu_header_; | ||||
|     bool printed_unpaired_header_; | ||||
|     bool uncovered_warnings_enabled_; | ||||
|  | ||||
|    private: | ||||
|     // Print a per-CU heading, once. | ||||
|     void CUHeading(); | ||||
|     // Print an unpaired function/line heading, once. | ||||
|     void UncoveredHeading(); | ||||
|   }; | ||||
|  | ||||
|   // Create a DWARF debugging info handler for a compilation unit | ||||
|   // within FILE_CONTEXT. This uses information received from the | ||||
|   // dwarf2reader::CompilationUnit DWARF parser to populate | ||||
|   // FILE_CONTEXT->module. Use LINE_READER to handle the compilation | ||||
|   // unit's line number data. Use REPORTER to report problems with the | ||||
|   // data we find. | ||||
|   DwarfCUToModule(FileContext *file_context, | ||||
|                   LineToModuleHandler *line_reader, | ||||
|                   WarningReporter *reporter); | ||||
|   ~DwarfCUToModule(); | ||||
|  | ||||
|   void ProcessAttributeSigned(enum DwarfAttribute attr, | ||||
|                               enum DwarfForm form, | ||||
|                               int64 data); | ||||
|   void ProcessAttributeUnsigned(enum DwarfAttribute attr, | ||||
|                                 enum DwarfForm form, | ||||
|                                 uint64 data); | ||||
|   void ProcessAttributeString(enum DwarfAttribute attr, | ||||
|                               enum DwarfForm form, | ||||
|                               const string &data); | ||||
|   bool EndAttributes(); | ||||
|   DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag); | ||||
|  | ||||
|   // Assign all our source Lines to the Functions that cover their | ||||
|   // addresses, and then add them to module_. | ||||
|   void Finish(); | ||||
|  | ||||
|   bool StartCompilationUnit(uint64 offset, uint8 address_size, | ||||
|                             uint8 offset_size, uint64 cu_length, | ||||
|                             uint8 dwarf_version); | ||||
|   bool StartRootDIE(uint64 offset, enum DwarfTag tag); | ||||
|  | ||||
|  private: | ||||
|   // Used internally by the handler. Full definitions are in | ||||
|   // dwarf_cu_to_module.cc. | ||||
|   struct CUContext; | ||||
|   struct DIEContext; | ||||
|   struct Specification; | ||||
|   class GenericDIEHandler; | ||||
|   class FuncHandler; | ||||
|   class NamedScopeHandler; | ||||
|  | ||||
|   // A map from section offsets to specifications. | ||||
|   typedef map<uint64, Specification> SpecificationByOffset; | ||||
|  | ||||
|   // Set this compilation unit's source language to LANGUAGE. | ||||
|   void SetLanguage(DwarfLanguage language); | ||||
|  | ||||
|   // Read source line information at OFFSET in the .debug_line | ||||
|   // section.  Record source files in module_, but record source lines | ||||
|   // in lines_; we apportion them to functions in | ||||
|   // AssignLinesToFunctions. | ||||
|   void ReadSourceLines(uint64 offset); | ||||
|  | ||||
|   // Assign the lines in lines_ to the individual line lists of the | ||||
|   // functions in functions_.  (DWARF line information maps an entire | ||||
|   // compilation unit at a time, and gives no indication of which | ||||
|   // lines belong to which functions, beyond their addresses.) | ||||
|   void AssignLinesToFunctions(); | ||||
|  | ||||
|   // The only reason cu_context_ and child_context_ are pointers is | ||||
|   // that we want to keep their definitions private to | ||||
|   // dwarf_cu_to_module.cc, instead of listing them all here. They are | ||||
|   // owned by this DwarfCUToModule: the constructor sets them, and the | ||||
|   // destructor deletes them. | ||||
|  | ||||
|   // The handler to use to handle line number data. | ||||
|   LineToModuleHandler *line_reader_; | ||||
|  | ||||
|   // This compilation unit's context. | ||||
|   scoped_ptr<CUContext> cu_context_; | ||||
|  | ||||
|   // A context for our children. | ||||
|   scoped_ptr<DIEContext> child_context_; | ||||
|  | ||||
|   // True if this compilation unit has source line information. | ||||
|   bool has_source_line_info_; | ||||
|  | ||||
|   // The offset of this compilation unit's line number information in | ||||
|   // the .debug_line section. | ||||
|   uint64 source_line_offset_; | ||||
|  | ||||
|   // The line numbers we have seen thus far.  We accumulate these here | ||||
|   // during parsing.  Then, in Finish, we call AssignLinesToFunctions | ||||
|   // to dole them out to the appropriate functions. | ||||
|   vector<Module::Line> lines_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_LINUX_DWARF_CU_TO_MODULE_H__ | ||||
							
								
								
									
										1780
									
								
								sdk/breakpad/common/dwarf_cu_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1780
									
								
								sdk/breakpad/common/dwarf_cu_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										143
									
								
								sdk/breakpad/common/dwarf_line_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								sdk/breakpad/common/dwarf_line_to_module.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf_line_to_module.cc: Implementation of DwarfLineToModule class. | ||||
| // See dwarf_line_to_module.h for details.  | ||||
|  | ||||
| #include <stdio.h> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/dwarf_line_to_module.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| // Trying to support Windows paths in a reasonable way adds a lot of | ||||
| // variations to test; it would be better to just put off dealing with | ||||
| // it until we actually have to deal with DWARF on Windows. | ||||
|  | ||||
| // Return true if PATH is an absolute path, false if it is relative. | ||||
| static bool PathIsAbsolute(const string &path) { | ||||
|   return (path.size() >= 1 && path[0] == '/'); | ||||
| } | ||||
|  | ||||
| static bool HasTrailingSlash(const string &path) { | ||||
|   return (path.size() >= 1 && path[path.size() - 1] == '/'); | ||||
| } | ||||
|  | ||||
| // If PATH is an absolute path, return PATH.  If PATH is a relative path, | ||||
| // treat it as relative to BASE and return the combined path. | ||||
| static string ExpandPath(const string &path, | ||||
|                          const string &base) { | ||||
|   if (PathIsAbsolute(path) || base.empty()) | ||||
|     return path; | ||||
|   return base + (HasTrailingSlash(base) ? "" : "/") + path; | ||||
| } | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) { | ||||
|   // Directory number zero is reserved to mean the compilation | ||||
|   // directory. Silently ignore attempts to redefine it. | ||||
|   if (dir_num != 0) | ||||
|     directories_[dir_num] = ExpandPath(name, compilation_dir_); | ||||
| } | ||||
|  | ||||
| void DwarfLineToModule::DefineFile(const string &name, int32 file_num, | ||||
|                                    uint32 dir_num, uint64 mod_time, | ||||
|                                    uint64 length) { | ||||
|   if (file_num == -1) | ||||
|     file_num = ++highest_file_number_; | ||||
|   else if (file_num > highest_file_number_) | ||||
|     highest_file_number_ = file_num; | ||||
|  | ||||
|   string dir_name; | ||||
|   if (dir_num == 0) { | ||||
|     // Directory number zero is the compilation directory, and is stored as | ||||
|     // an attribute on the compilation unit, rather than in the program table. | ||||
|     dir_name = compilation_dir_; | ||||
|   } else { | ||||
|     DirectoryTable::const_iterator directory_it = directories_.find(dir_num); | ||||
|     if (directory_it != directories_.end()) { | ||||
|       dir_name = directory_it->second; | ||||
|     } else { | ||||
|       if (!warned_bad_directory_number_) { | ||||
|         fprintf(stderr, "warning: DWARF line number data refers to undefined" | ||||
|                 " directory numbers\n"); | ||||
|         warned_bad_directory_number_ = true; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   string full_name = ExpandPath(name, dir_name); | ||||
|  | ||||
|   // Find a Module::File object of the given name, and add it to the | ||||
|   // file table. | ||||
|   files_[file_num] = module_->FindFile(full_name); | ||||
| } | ||||
|  | ||||
| void DwarfLineToModule::AddLine(uint64 address, uint64 length, | ||||
|                                 uint32 file_num, uint32 line_num, | ||||
|                                 uint32 column_num) { | ||||
|   if (length == 0) | ||||
|     return; | ||||
|  | ||||
|   // Clip lines not to extend beyond the end of the address space. | ||||
|   if (address + length < address) | ||||
|     length = -address; | ||||
|  | ||||
|   // Should we omit this line? (See the comments for omitted_line_end_.) | ||||
|   if (address == 0 || address == omitted_line_end_) { | ||||
|     omitted_line_end_ = address + length; | ||||
|     return; | ||||
|   } else { | ||||
|     omitted_line_end_ = 0; | ||||
|   } | ||||
|  | ||||
|   // Find the source file being referred to. | ||||
|   Module::File *file = files_[file_num]; | ||||
|   if (!file) { | ||||
|     if (!warned_bad_file_number_) { | ||||
|       fprintf(stderr, "warning: DWARF line number data refers to " | ||||
|               "undefined file numbers\n"); | ||||
|       warned_bad_file_number_ = true; | ||||
|     } | ||||
|     return; | ||||
|   } | ||||
|   Module::Line line; | ||||
|   line.address = address; | ||||
|   // We set the size when we get the next line or the EndSequence call. | ||||
|   line.size = length; | ||||
|   line.file = file; | ||||
|   line.number = line_num; | ||||
|   lines_->push_back(line); | ||||
| } | ||||
|  | ||||
| } // namespace google_breakpad | ||||
							
								
								
									
										188
									
								
								sdk/breakpad/common/dwarf_line_to_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								sdk/breakpad/common/dwarf_line_to_module.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,188 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // The DwarfLineToModule class accepts line number information from a | ||||
| // DWARF parser and adds it to a google_breakpad::Module. The Module | ||||
| // can write that data out as a Breakpad symbol file. | ||||
|  | ||||
| #ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H | ||||
| #define COMMON_LINUX_DWARF_LINE_TO_MODULE_H | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/module.h" | ||||
| #include "common/dwarf/dwarf2reader.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // A class for producing a vector of google_breakpad::Module::Line | ||||
| // instances from parsed DWARF line number data.   | ||||
| // | ||||
| // An instance of this class can be provided as a handler to a | ||||
| // dwarf2reader::LineInfo DWARF line number information parser. The | ||||
| // handler accepts source location information from the parser and | ||||
| // uses it to produce a vector of google_breakpad::Module::Line | ||||
| // objects, referring to google_breakpad::Module::File objects added | ||||
| // to a particular google_breakpad::Module. | ||||
| // | ||||
| // GNU toolchain omitted sections support: | ||||
| // ====================================== | ||||
| // | ||||
| // Given the right options, the GNU toolchain will omit unreferenced | ||||
| // functions from the final executable. Unfortunately, when it does so, it | ||||
| // does not remove the associated portions of the DWARF line number | ||||
| // program; instead, it gives the DW_LNE_set_address instructions referring | ||||
| // to the now-deleted code addresses of zero. Given this input, the DWARF | ||||
| // line parser will call AddLine with a series of lines starting at address | ||||
| // zero. For example, here is the output from 'readelf -wl' for a program | ||||
| // with four functions, the first three of which have been omitted: | ||||
| // | ||||
| //   Line Number Statements: | ||||
| //    Extended opcode 2: set Address to 0x0 | ||||
| //    Advance Line by 14 to 15 | ||||
| //    Copy | ||||
| //    Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 | ||||
| //    Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 | ||||
| //    Advance PC by 2 to 0xd | ||||
| //    Extended opcode 1: End of Sequence | ||||
| //  | ||||
| //    Extended opcode 2: set Address to 0x0 | ||||
| //    Advance Line by 14 to 15 | ||||
| //    Copy | ||||
| //    Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 | ||||
| //    Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 | ||||
| //    Advance PC by 2 to 0xd | ||||
| //    Extended opcode 1: End of Sequence | ||||
| //  | ||||
| //    Extended opcode 2: set Address to 0x0 | ||||
| //    Advance Line by 19 to 20 | ||||
| //    Copy | ||||
| //    Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21 | ||||
| //    Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22 | ||||
| //    Advance PC by 2 to 0xa | ||||
| //    Extended opcode 1: End of Sequence | ||||
| //  | ||||
| //    Extended opcode 2: set Address to 0x80483a4 | ||||
| //    Advance Line by 23 to 24 | ||||
| //    Copy | ||||
| //    Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25 | ||||
| //    Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26 | ||||
| //    Advance PC by 6 to 0x80483bd | ||||
| //    Extended opcode 1: End of Sequence | ||||
| // | ||||
| // Instead of collecting runs of lines describing code that is not there, | ||||
| // we try to recognize and drop them. Since the linker doesn't explicitly | ||||
| // distinguish references to dropped sections from genuine references to | ||||
| // code at address zero, we must use a heuristic. We have chosen: | ||||
| // | ||||
| // - If a line starts at address zero, omit it. (On the platforms | ||||
| //   breakpad targets, it is extremely unlikely that there will be code | ||||
| //   at address zero.) | ||||
| // | ||||
| // - If a line starts immediately after an omitted line, omit it too. | ||||
| class DwarfLineToModule: public dwarf2reader::LineInfoHandler { | ||||
|  public: | ||||
|   // As the DWARF line info parser passes us line records, add source | ||||
|   // files to MODULE, and add all lines to the end of LINES. LINES | ||||
|   // need not be empty. If the parser hands us a zero-length line, we | ||||
|   // omit it. If the parser hands us a line that extends beyond the | ||||
|   // end of the address space, we clip it. It's up to our client to | ||||
|   // sort out which lines belong to which functions; we don't add them | ||||
|   // to any particular function in MODULE ourselves. | ||||
|   DwarfLineToModule(Module *module, const string& compilation_dir, | ||||
|                     vector<Module::Line> *lines) | ||||
|       : module_(module), | ||||
|         compilation_dir_(compilation_dir), | ||||
|         lines_(lines), | ||||
|         highest_file_number_(-1), | ||||
|         omitted_line_end_(0), | ||||
|         warned_bad_file_number_(false), | ||||
|         warned_bad_directory_number_(false) { } | ||||
|    | ||||
|   ~DwarfLineToModule() { } | ||||
|  | ||||
|   void DefineDir(const string &name, uint32 dir_num); | ||||
|   void DefineFile(const string &name, int32 file_num, | ||||
|                   uint32 dir_num, uint64 mod_time, | ||||
|                   uint64 length); | ||||
|   void AddLine(uint64 address, uint64 length, | ||||
|                uint32 file_num, uint32 line_num, uint32 column_num); | ||||
|  | ||||
|  private: | ||||
|  | ||||
|   typedef std::map<uint32, string> DirectoryTable; | ||||
|   typedef std::map<uint32, Module::File *> FileTable; | ||||
|  | ||||
|   // The module we're contributing debugging info to. Owned by our | ||||
|   // client. | ||||
|   Module *module_; | ||||
|  | ||||
|   // The compilation directory for the current compilation unit whose | ||||
|   // lines are being accumulated. | ||||
|   string compilation_dir_; | ||||
|  | ||||
|   // The vector of lines we're accumulating. Owned by our client. | ||||
|   // | ||||
|   // In a Module, as in a breakpad symbol file, lines belong to | ||||
|   // specific functions, but DWARF simply assigns lines to addresses; | ||||
|   // one must infer the line/function relationship using the | ||||
|   // functions' beginning and ending addresses. So we can't add these | ||||
|   // to the appropriate function from module_ until we've read the | ||||
|   // function info as well. Instead, we accumulate lines here, and let | ||||
|   // whoever constructed this sort it all out. | ||||
|   vector<Module::Line> *lines_; | ||||
|  | ||||
|   // A table mapping directory numbers to paths. | ||||
|   DirectoryTable directories_; | ||||
|  | ||||
|   // A table mapping file numbers to Module::File pointers. | ||||
|   FileTable files_; | ||||
|  | ||||
|   // The highest file number we've seen so far, or -1 if we've seen | ||||
|   // none.  Used for dynamically defined file numbers. | ||||
|   int32 highest_file_number_; | ||||
|    | ||||
|   // This is the ending address of the last line we omitted, or zero if we | ||||
|   // didn't omit the previous line. It is zero before we have received any | ||||
|   // AddLine calls. | ||||
|   uint64 omitted_line_end_; | ||||
|  | ||||
|   // True if we've warned about: | ||||
|   bool warned_bad_file_number_; // bad file numbers | ||||
|   bool warned_bad_directory_number_; // bad directory numbers | ||||
| }; | ||||
|  | ||||
| } // namespace google_breakpad | ||||
|  | ||||
| #endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H | ||||
							
								
								
									
										391
									
								
								sdk/breakpad/common/dwarf_line_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										391
									
								
								sdk/breakpad/common/dwarf_line_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,391 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule. | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/dwarf_line_to_module.h" | ||||
|  | ||||
| using std::vector; | ||||
|  | ||||
| using google_breakpad::DwarfLineToModule; | ||||
| using google_breakpad::Module; | ||||
| using google_breakpad::Module; | ||||
|  | ||||
| TEST(SimpleModule, One) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); | ||||
|   h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, | ||||
|             0x4c090cbf, 0x1cf9fe0d); | ||||
|  | ||||
|   vector<Module::File *> files; | ||||
|   m.GetFiles(&files); | ||||
|   EXPECT_EQ(1U, files.size()); | ||||
|   EXPECT_STREQ("/file1", files[0]->name.c_str()); | ||||
|  | ||||
|   EXPECT_EQ(1U, lines.size()); | ||||
|   EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address); | ||||
|   EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size); | ||||
|   EXPECT_TRUE(lines[0].file == files[0]); | ||||
|   EXPECT_EQ(0x4c090cbf, lines[0].number); | ||||
| } | ||||
|  | ||||
| TEST(SimpleModule, Many) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("directory1", 0x838299ab); | ||||
|   h.DefineDir("directory2", 0xf85de023); | ||||
|   h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0); | ||||
|   h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0); | ||||
|   h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0); | ||||
|   h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0); | ||||
|   h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a, | ||||
|             0x15b0f0a9U, 0x3ff5abd6U); | ||||
|   h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4, | ||||
|             0x4d259ce9U, 0x41c5ee32U); | ||||
|   h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56, | ||||
|             0x1ee9fa4fU, 0xbf70e46aU); | ||||
|   h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c, | ||||
|             0x77fc280eU, 0x2c4a728cU); | ||||
|   h.DefineFile("file3", -1, 0, 0, 0); | ||||
|   h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5, | ||||
|             0x75047044U, 0xb6a0016cU); | ||||
|  | ||||
|   vector<Module::File *> files; | ||||
|   m.GetFiles(&files); | ||||
|   ASSERT_EQ(5U, files.size()); | ||||
|   EXPECT_STREQ("/directory1/file1", files[0]->name.c_str()); | ||||
|   EXPECT_STREQ("/directory1/file2", files[1]->name.c_str()); | ||||
|   EXPECT_STREQ("/directory2/file1", files[2]->name.c_str()); | ||||
|   EXPECT_STREQ("/directory2/file2", files[3]->name.c_str()); | ||||
|   EXPECT_STREQ("/file3",            files[4]->name.c_str()); | ||||
|  | ||||
|   ASSERT_EQ(5U, lines.size()); | ||||
|  | ||||
|   EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address); | ||||
|   EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size); | ||||
|   EXPECT_TRUE(lines[0].file == files[0]); | ||||
|   EXPECT_EQ(0x15b0f0a9, lines[0].number); | ||||
|  | ||||
|   EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address); | ||||
|   EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size); | ||||
|   EXPECT_TRUE(lines[1].file == files[2]); | ||||
|   EXPECT_EQ(0x4d259ce9, lines[1].number); | ||||
|  | ||||
|   EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address); | ||||
|   EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size); | ||||
|   EXPECT_TRUE(lines[2].file == files[1]); | ||||
|   EXPECT_EQ(0x1ee9fa4f, lines[2].number); | ||||
|  | ||||
|   EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address); | ||||
|   EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size); | ||||
|   EXPECT_TRUE(lines[3].file == files[3]); | ||||
|   EXPECT_EQ(0x77fc280e, lines[3].number); | ||||
|  | ||||
|   EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address); | ||||
|   EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size); | ||||
|   EXPECT_TRUE(lines[4].file == files[4]); | ||||
|   EXPECT_EQ(0x75047044, lines[4].number); | ||||
| } | ||||
|  | ||||
| TEST(Filenames, Absolute) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("directory1", 1); | ||||
|   h.DefineFile("/absolute", 1, 1, 0, 0); | ||||
|  | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   vector<Module::File *> files; | ||||
|   m.GetFiles(&files); | ||||
|   ASSERT_EQ(1U, files.size()); | ||||
|   EXPECT_STREQ("/absolute", files[0]->name.c_str()); | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_TRUE(lines[0].file == files[0]); | ||||
| } | ||||
|  | ||||
| TEST(Filenames, Relative) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("directory1", 1); | ||||
|   h.DefineFile("relative", 1, 1, 0, 0); | ||||
|  | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   vector<Module::File *> files; | ||||
|   m.GetFiles(&files); | ||||
|   ASSERT_EQ(1U, files.size()); | ||||
|   EXPECT_STREQ("/directory1/relative", files[0]->name.c_str()); | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_TRUE(lines[0].file == files[0]); | ||||
| } | ||||
|  | ||||
| TEST(Filenames, StrangeFile) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("directory1", 1); | ||||
|   h.DefineFile("", 1, 1, 0, 0); | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("/directory1/", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(Filenames, StrangeDirectory) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("", 1); | ||||
|   h.DefineFile("file1", 1, 1, 0, 0); | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("/file1", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| TEST(Filenames, StrangeDirectoryAndFile) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("", 1); | ||||
|   h.DefineFile("", 1, 1, 0, 0); | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("/", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| // We should use the compilation directory when encountering a file for | ||||
| // directory number zero. | ||||
| TEST(Filenames, DirectoryZeroFileIsRelativeToCompilationDir) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "src/build", &lines); | ||||
|  | ||||
|   h.DefineDir("Dir", 1); | ||||
|   h.DefineFile("File", 1, 0, 0, 0); | ||||
|  | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("src/build/File", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| // We should treat non-absolute directories as relative to the compilation | ||||
| // directory. | ||||
| TEST(Filenames, IncludeDirectoryRelativeToDirectoryZero) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "src/build", &lines); | ||||
|  | ||||
|   h.DefineDir("Dir", 1); | ||||
|   h.DefineFile("File", 1, 1, 0, 0); | ||||
|  | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("src/build/Dir/File", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| // We should treat absolute directories as absolute, and not relative to | ||||
| // the compilation dir. | ||||
| TEST(Filenames, IncludeDirectoryAbsolute) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "src/build", &lines); | ||||
|  | ||||
|   h.DefineDir("/Dir", 1); | ||||
|   h.DefineFile("File", 1, 1, 0, 0); | ||||
|  | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("/Dir/File", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| // We should silently ignore attempts to define directory number zero, | ||||
| // since that is always the compilation directory. | ||||
| TEST(ModuleErrors, DirectoryZero) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("directory0", 0); // should be ignored | ||||
|   h.DefineFile("relative", 1, 0, 0, 0); | ||||
|  | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("/relative", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| // We should refuse to add lines with bogus file numbers. We should | ||||
| // produce only one warning, however. | ||||
| TEST(ModuleErrors, BadFileNumber) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("relative", 1, 0, 0, 0); | ||||
|   h.AddLine(1, 1, 2, 0, 0); // bad file number | ||||
|   h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning) | ||||
|  | ||||
|   EXPECT_EQ(0U, lines.size()); | ||||
| } | ||||
|  | ||||
| // We should treat files with bogus directory numbers as relative to | ||||
| // the compilation unit. | ||||
| TEST(ModuleErrors, BadDirectoryNumber) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineDir("directory1", 1); | ||||
|   h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number | ||||
|   h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning) | ||||
|   h.AddLine(1, 1, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str()); | ||||
| } | ||||
|  | ||||
| // We promise not to report empty lines. | ||||
| TEST(ModuleErrors, EmptyLine) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("filename1", 1, 0, 0, 0); | ||||
|   h.AddLine(1, 0, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(0U, lines.size()); | ||||
| }   | ||||
|  | ||||
| // We are supposed to clip lines that extend beyond the end of the | ||||
| // address space. | ||||
| TEST(ModuleErrors, BigLine) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("filename1", 1, 0, 0, 0); | ||||
|   h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0); | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_EQ(1U, lines[0].size); | ||||
| }   | ||||
|  | ||||
| // The 'Omitted' tests verify that we correctly omit line information | ||||
| // for code in sections that the linker has dropped. See "GNU | ||||
| // toolchain omitted sections support" at the top of the | ||||
| // DwarfLineToModule class. | ||||
|  | ||||
| TEST(Omitted, DroppedThenGood) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("filename1", 1, 0, 0, 0); | ||||
|   h.AddLine(0,  10, 1, 83816211, 0);   // should be omitted | ||||
|   h.AddLine(20, 10, 1, 13059195, 0);   // should be recorded | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_EQ(13059195, lines[0].number); | ||||
| } | ||||
|  | ||||
| TEST(Omitted, GoodThenDropped) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("filename1", 1, 0, 0, 0); | ||||
|   h.AddLine(0x9dd6a372, 10, 1, 41454594, 0);   // should be recorded | ||||
|   h.AddLine(0,  10, 1, 44793413, 0);           // should be omitted | ||||
|  | ||||
|   ASSERT_EQ(1U, lines.size()); | ||||
|   EXPECT_EQ(41454594, lines[0].number); | ||||
| } | ||||
|  | ||||
| TEST(Omitted, Mix1) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("filename1", 1, 0, 0, 0); | ||||
|   h.AddLine(0x679ed72f,  10,   1, 58932642, 0);   // should be recorded | ||||
|   h.AddLine(0xdfb5a72d,  10,   1, 39847385, 0);   // should be recorded | ||||
|   h.AddLine(0,           0x78, 1, 23053829, 0);   // should be omitted | ||||
|   h.AddLine(0x78,        0x6a, 1, 65317783, 0);   // should be omitted | ||||
|   h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0);   // should be omitted | ||||
|   h.AddLine(0x9fe0cea5,  10,   1, 91806582, 0);   // should be recorded | ||||
|   h.AddLine(0x7e41a109,  10,   1, 56169221, 0);   // should be recorded | ||||
|  | ||||
|   ASSERT_EQ(4U, lines.size()); | ||||
|   EXPECT_EQ(58932642, lines[0].number); | ||||
|   EXPECT_EQ(39847385, lines[1].number); | ||||
|   EXPECT_EQ(91806582, lines[2].number); | ||||
|   EXPECT_EQ(56169221, lines[3].number); | ||||
| } | ||||
|  | ||||
| TEST(Omitted, Mix2) { | ||||
|   Module m("name", "os", "architecture", "id"); | ||||
|   vector<Module::Line> lines; | ||||
|   DwarfLineToModule h(&m, "/", &lines); | ||||
|  | ||||
|   h.DefineFile("filename1", 1, 0, 0, 0); | ||||
|   h.AddLine(0,           0xf2, 1, 58802211, 0);   // should be omitted | ||||
|   h.AddLine(0xf2,        0xb9, 1, 78958222, 0);   // should be omitted | ||||
|   h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0);   // should be omitted | ||||
|   h.AddLine(0x4e4d271e,  9,    1, 67355743, 0);   // should be recorded | ||||
|   h.AddLine(0xdfb5a72d,  30,   1, 23365776, 0);   // should be recorded | ||||
|   h.AddLine(0,           0x64, 1, 76196762, 0);   // should be omitted | ||||
|   h.AddLine(0x64,        0x33, 1, 71066611, 0);   // should be omitted | ||||
|   h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0);   // should be omitted | ||||
|  | ||||
|   ASSERT_EQ(2U, lines.size()); | ||||
|   EXPECT_EQ(67355743, lines[0].number); | ||||
|   EXPECT_EQ(23365776, lines[1].number); | ||||
| } | ||||
							
								
								
									
										83
									
								
								sdk/breakpad/common/language.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								sdk/breakpad/common/language.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // language.cc: Subclasses and singletons for google_breakpad::Language. | ||||
| // See language.h for details. | ||||
|  | ||||
| #include "common/language.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // C++ language-specific operations. | ||||
| class CPPLanguage: public Language { | ||||
|  public: | ||||
|   CPPLanguage() {} | ||||
|   string MakeQualifiedName(const string &parent_name, | ||||
|                            const string &name) const { | ||||
|     if (parent_name.empty()) | ||||
|       return name; | ||||
|     else | ||||
|       return parent_name + "::" + name; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| CPPLanguage CPPLanguageSingleton; | ||||
|  | ||||
| // Java language-specific operations. | ||||
| class JavaLanguage: public Language { | ||||
|  public: | ||||
|   string MakeQualifiedName(const string &parent_name, | ||||
|                            const string &name) const { | ||||
|     if (parent_name.empty()) | ||||
|       return name; | ||||
|     else | ||||
|       return parent_name + "." + name; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| JavaLanguage JavaLanguageSingleton; | ||||
|  | ||||
| // Assembler language-specific operations. | ||||
| class AssemblerLanguage: public Language { | ||||
|   bool HasFunctions() const { return false; } | ||||
|   string MakeQualifiedName(const string &parent_name, | ||||
|                            const string &name) const { | ||||
|     return name; | ||||
|   } | ||||
| }; | ||||
|  | ||||
| AssemblerLanguage AssemblerLanguageSingleton; | ||||
|  | ||||
| const Language * const Language::CPlusPlus = &CPPLanguageSingleton; | ||||
| const Language * const Language::Java = &JavaLanguageSingleton; | ||||
| const Language * const Language::Assembler = &AssemblerLanguageSingleton; | ||||
|  | ||||
| } // namespace google_breakpad | ||||
							
								
								
									
										88
									
								
								sdk/breakpad/common/language.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								sdk/breakpad/common/language.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,88 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // language.h: Define google_breakpad::Language. Instances of | ||||
| // subclasses of this class provide language-appropriate operations | ||||
| // for the Breakpad symbol dumper. | ||||
|  | ||||
| #ifndef COMMON_LINUX_LANGUAGE_H__ | ||||
| #define COMMON_LINUX_LANGUAGE_H__ | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // An abstract base class for language-specific operations. We choose | ||||
| // an instance of a subclass of this when we find the CU's language. | ||||
| // This class's definitions are appropriate for CUs with no specified | ||||
| // language. | ||||
| class Language { | ||||
|  public: | ||||
|   // A base class destructor should be either public and virtual, | ||||
|   // or protected and nonvirtual. | ||||
|   virtual ~Language() {} | ||||
|  | ||||
|   // Return true if this language has functions to which we can assign | ||||
|   // line numbers. (Debugging info for assembly language, for example, | ||||
|   // can have source location information, but does not have functions | ||||
|   // recorded using DW_TAG_subprogram DIEs.) | ||||
|   virtual bool HasFunctions() const { return true; } | ||||
|  | ||||
|   // Construct a fully-qualified, language-appropriate form of NAME, | ||||
|   // given that PARENT_NAME is the name of the construct enclosing | ||||
|   // NAME. If PARENT_NAME is the empty string, then NAME is a | ||||
|   // top-level name. | ||||
|   // | ||||
|   // This API sort of assumes that a fully-qualified name is always | ||||
|   // some simple textual composition of the unqualified name and its | ||||
|   // parent's name, and that we don't need to know anything else about | ||||
|   // the parent or the child (say, their DIEs' tags) to do the job. | ||||
|   // This is true for the languages we support at the moment, and | ||||
|   // keeps things concrete. Perhaps a more refined operation would | ||||
|   // take into account the parent and child DIE types, allow languages | ||||
|   // to use their own data type for complex parent names, etc. But if | ||||
|   // C++ doesn't need all that, who would? | ||||
|   virtual string MakeQualifiedName (const string &parent_name, | ||||
|                                     const string &name) const = 0; | ||||
|  | ||||
|   // Instances for specific languages. | ||||
|   static const Language * const CPlusPlus, | ||||
|                         * const Java, | ||||
|                         * const Assembler; | ||||
| }; | ||||
|  | ||||
| } // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_LINUX_LANGUAGE_H__ | ||||
							
								
								
									
										911
									
								
								sdk/breakpad/common/linux/dump_symbols.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										911
									
								
								sdk/breakpad/common/linux/dump_symbols.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,911 @@ | ||||
| // Copyright (c) 2011 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Restructured in 2009 by: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> | ||||
|  | ||||
| // dump_symbols.cc: implement google_breakpad::WriteSymbolFile: | ||||
| // Find all the debugging info in a file and dump it as a Breakpad symbol file. | ||||
|  | ||||
| #include "common/linux/dump_symbols.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <elf.h> | ||||
| #include <errno.h> | ||||
| #include <fcntl.h> | ||||
| #include <link.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <string.h> | ||||
| #include <sys/mman.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <iostream> | ||||
| #include <set> | ||||
| #include <string> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/dwarf/bytereader-inl.h" | ||||
| #include "common/dwarf/dwarf2diehandler.h" | ||||
| #include "common/dwarf_cfi_to_module.h" | ||||
| #include "common/dwarf_cu_to_module.h" | ||||
| #include "common/dwarf_line_to_module.h" | ||||
| #include "common/linux/elfutils.h" | ||||
| #include "common/linux/elfutils-inl.h" | ||||
| #include "common/linux/elf_symbols_to_module.h" | ||||
| #include "common/linux/file_id.h" | ||||
| #include "common/module.h" | ||||
| #include "common/scoped_ptr.h" | ||||
| #ifndef NO_STABS_SUPPORT | ||||
| #include "common/stabs_reader.h" | ||||
| #include "common/stabs_to_module.h" | ||||
| #endif | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| // This namespace contains helper functions. | ||||
| namespace { | ||||
|  | ||||
| using google_breakpad::DumpOptions; | ||||
| using google_breakpad::DwarfCFIToModule; | ||||
| using google_breakpad::DwarfCUToModule; | ||||
| using google_breakpad::DwarfLineToModule; | ||||
| using google_breakpad::ElfClass; | ||||
| using google_breakpad::ElfClass32; | ||||
| using google_breakpad::ElfClass64; | ||||
| using google_breakpad::FindElfSectionByName; | ||||
| using google_breakpad::GetOffset; | ||||
| using google_breakpad::IsValidElf; | ||||
| using google_breakpad::Module; | ||||
| #ifndef NO_STABS_SUPPORT | ||||
| using google_breakpad::StabsToModule; | ||||
| #endif | ||||
| using google_breakpad::scoped_ptr; | ||||
|  | ||||
| // | ||||
| // FDWrapper | ||||
| // | ||||
| // Wrapper class to make sure opened file is closed. | ||||
| // | ||||
| class FDWrapper { | ||||
|  public: | ||||
|   explicit FDWrapper(int fd) : | ||||
|     fd_(fd) {} | ||||
|   ~FDWrapper() { | ||||
|     if (fd_ != -1) | ||||
|       close(fd_); | ||||
|   } | ||||
|   int get() { | ||||
|     return fd_; | ||||
|   } | ||||
|   int release() { | ||||
|     int fd = fd_; | ||||
|     fd_ = -1; | ||||
|     return fd; | ||||
|   } | ||||
|  private: | ||||
|   int fd_; | ||||
| }; | ||||
|  | ||||
| // | ||||
| // MmapWrapper | ||||
| // | ||||
| // Wrapper class to make sure mapped regions are unmapped. | ||||
| // | ||||
| class MmapWrapper { | ||||
|  public: | ||||
|   MmapWrapper() : is_set_(false) {} | ||||
|   ~MmapWrapper() { | ||||
|     if (is_set_ && base_ != NULL) { | ||||
|       assert(size_ > 0); | ||||
|       munmap(base_, size_); | ||||
|     } | ||||
|   } | ||||
|   void set(void *mapped_address, size_t mapped_size) { | ||||
|     is_set_ = true; | ||||
|     base_ = mapped_address; | ||||
|     size_ = mapped_size; | ||||
|   } | ||||
|   void release() { | ||||
|     assert(is_set_); | ||||
|     is_set_ = false; | ||||
|     base_ = NULL; | ||||
|     size_ = 0; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   bool is_set_; | ||||
|   void *base_; | ||||
|   size_t size_; | ||||
| }; | ||||
|  | ||||
| // Find the preferred loading address of the binary. | ||||
| template<typename ElfClass> | ||||
| typename ElfClass::Addr GetLoadingAddress( | ||||
|     const typename ElfClass::Phdr* program_headers, | ||||
|     int nheader) { | ||||
|   typedef typename ElfClass::Phdr Phdr; | ||||
|  | ||||
|   // For non-PIC executables (e_type == ET_EXEC), the load address is | ||||
|   // the start address of the first PT_LOAD segment.  (ELF requires | ||||
|   // the segments to be sorted by load address.)  For PIC executables | ||||
|   // and dynamic libraries (e_type == ET_DYN), this address will | ||||
|   // normally be zero. | ||||
|   for (int i = 0; i < nheader; ++i) { | ||||
|     const Phdr& header = program_headers[i]; | ||||
|     if (header.p_type == PT_LOAD) | ||||
|       return header.p_vaddr; | ||||
|   } | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| #ifndef NO_STABS_SUPPORT | ||||
| template<typename ElfClass> | ||||
| bool LoadStabs(const typename ElfClass::Ehdr* elf_header, | ||||
|                const typename ElfClass::Shdr* stab_section, | ||||
|                const typename ElfClass::Shdr* stabstr_section, | ||||
|                const bool big_endian, | ||||
|                Module* module) { | ||||
|   // A callback object to handle data from the STABS reader. | ||||
|   StabsToModule handler(module); | ||||
|   // Find the addresses of the STABS data, and create a STABS reader object. | ||||
|   // On Linux, STABS entries always have 32-bit values, regardless of the | ||||
|   // address size of the architecture whose code they're describing, and | ||||
|   // the strings are always "unitized". | ||||
|   const uint8_t* stabs = | ||||
|       GetOffset<ElfClass, uint8_t>(elf_header, stab_section->sh_offset); | ||||
|   const uint8_t* stabstr = | ||||
|       GetOffset<ElfClass, uint8_t>(elf_header, stabstr_section->sh_offset); | ||||
|   google_breakpad::StabsReader reader(stabs, stab_section->sh_size, | ||||
|                                       stabstr, stabstr_section->sh_size, | ||||
|                                       big_endian, 4, true, &handler); | ||||
|   // Read the STABS data, and do post-processing. | ||||
|   if (!reader.Process()) | ||||
|     return false; | ||||
|   handler.Finalize(); | ||||
|   return true; | ||||
| } | ||||
| #endif  // NO_STABS_SUPPORT | ||||
|  | ||||
| // A line-to-module loader that accepts line number info parsed by | ||||
| // dwarf2reader::LineInfo and populates a Module and a line vector | ||||
| // with the results. | ||||
| class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler { | ||||
|  public: | ||||
|   // Create a line-to-module converter using BYTE_READER. | ||||
|   explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader) | ||||
|       : byte_reader_(byte_reader) { } | ||||
|   void StartCompilationUnit(const string& compilation_dir) { | ||||
|     compilation_dir_ = compilation_dir; | ||||
|   } | ||||
|   void ReadProgram(const char *program, uint64 length, | ||||
|                    Module *module, std::vector<Module::Line> *lines) { | ||||
|     DwarfLineToModule handler(module, compilation_dir_, lines); | ||||
|     dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); | ||||
|     parser.Start(); | ||||
|   } | ||||
|  private: | ||||
|   string compilation_dir_; | ||||
|   dwarf2reader::ByteReader *byte_reader_; | ||||
| }; | ||||
|  | ||||
| template<typename ElfClass> | ||||
| bool LoadDwarf(const string& dwarf_filename, | ||||
|                const typename ElfClass::Ehdr* elf_header, | ||||
|                const bool big_endian, | ||||
|                bool handle_inter_cu_refs, | ||||
|                Module* module) { | ||||
|   typedef typename ElfClass::Shdr Shdr; | ||||
|  | ||||
|   const dwarf2reader::Endianness endianness = big_endian ? | ||||
|       dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; | ||||
|   dwarf2reader::ByteReader byte_reader(endianness); | ||||
|  | ||||
|   // Construct a context for this file. | ||||
|   DwarfCUToModule::FileContext file_context(dwarf_filename, | ||||
|                                             module, | ||||
|                                             handle_inter_cu_refs); | ||||
|  | ||||
|   // Build a map of the ELF file's sections. | ||||
|   const Shdr* sections = | ||||
|       GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); | ||||
|   int num_sections = elf_header->e_shnum; | ||||
|   const Shdr* section_names = sections + elf_header->e_shstrndx; | ||||
|   for (int i = 0; i < num_sections; i++) { | ||||
|     const Shdr* section = §ions[i]; | ||||
|     string name = GetOffset<ElfClass, char>(elf_header, | ||||
|                                             section_names->sh_offset) + | ||||
|                   section->sh_name; | ||||
|     const char* contents = GetOffset<ElfClass, char>(elf_header, | ||||
|                                                      section->sh_offset); | ||||
|     file_context.AddSectionToSectionMap(name, contents, section->sh_size); | ||||
|   } | ||||
|  | ||||
|   // Parse all the compilation units in the .debug_info section. | ||||
|   DumperLineToModule line_to_module(&byte_reader); | ||||
|   dwarf2reader::SectionMap::const_iterator debug_info_entry = | ||||
|       file_context.section_map().find(".debug_info"); | ||||
|   assert(debug_info_entry != file_context.section_map().end()); | ||||
|   const std::pair<const char*, uint64>& debug_info_section = | ||||
|       debug_info_entry->second; | ||||
|   // This should never have been called if the file doesn't have a | ||||
|   // .debug_info section. | ||||
|   assert(debug_info_section.first); | ||||
|   uint64 debug_info_length = debug_info_section.second; | ||||
|   for (uint64 offset = 0; offset < debug_info_length;) { | ||||
|     // Make a handler for the root DIE that populates MODULE with the | ||||
|     // data that was found. | ||||
|     DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); | ||||
|     DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); | ||||
|     // Make a Dwarf2Handler that drives the DIEHandler. | ||||
|     dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); | ||||
|     // Make a DWARF parser for the compilation unit at OFFSET. | ||||
|     dwarf2reader::CompilationUnit reader(file_context.section_map(), | ||||
|                                          offset, | ||||
|                                          &byte_reader, | ||||
|                                          &die_dispatcher); | ||||
|     // Process the entire compilation unit; get the offset of the next. | ||||
|     offset += reader.Start(); | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // Fill REGISTER_NAMES with the register names appropriate to the | ||||
| // machine architecture given in HEADER, indexed by the register | ||||
| // numbers used in DWARF call frame information. Return true on | ||||
| // success, or false if HEADER's machine architecture is not | ||||
| // supported. | ||||
| template<typename ElfClass> | ||||
| bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, | ||||
|                            std::vector<string>* register_names) { | ||||
|   switch (elf_header->e_machine) { | ||||
|     case EM_386: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::I386(); | ||||
|       return true; | ||||
|     case EM_ARM: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::ARM(); | ||||
|       return true; | ||||
|     case EM_MIPS: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::MIPS(); | ||||
|       return true; | ||||
|     case EM_X86_64: | ||||
|       *register_names = DwarfCFIToModule::RegisterNames::X86_64(); | ||||
|       return true; | ||||
|     default: | ||||
|       return false; | ||||
|   } | ||||
| } | ||||
|  | ||||
| template<typename ElfClass> | ||||
| bool LoadDwarfCFI(const string& dwarf_filename, | ||||
|                   const typename ElfClass::Ehdr* elf_header, | ||||
|                   const char* section_name, | ||||
|                   const typename ElfClass::Shdr* section, | ||||
|                   const bool eh_frame, | ||||
|                   const typename ElfClass::Shdr* got_section, | ||||
|                   const typename ElfClass::Shdr* text_section, | ||||
|                   const bool big_endian, | ||||
|                   Module* module) { | ||||
|   // Find the appropriate set of register names for this file's | ||||
|   // architecture. | ||||
|   std::vector<string> register_names; | ||||
|   if (!DwarfCFIRegisterNames<ElfClass>(elf_header, ®ister_names)) { | ||||
|     fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" | ||||
|             " cannot convert DWARF call frame information\n", | ||||
|             dwarf_filename.c_str(), elf_header->e_machine); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   const dwarf2reader::Endianness endianness = big_endian ? | ||||
|       dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE; | ||||
|  | ||||
|   // Find the call frame information and its size. | ||||
|   const char* cfi = | ||||
|       GetOffset<ElfClass, char>(elf_header, section->sh_offset); | ||||
|   size_t cfi_size = section->sh_size; | ||||
|  | ||||
|   // Plug together the parser, handler, and their entourages. | ||||
|   DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); | ||||
|   DwarfCFIToModule handler(module, register_names, &module_reporter); | ||||
|   dwarf2reader::ByteReader byte_reader(endianness); | ||||
|  | ||||
|   byte_reader.SetAddressSize(ElfClass::kAddrSize); | ||||
|  | ||||
|   // Provide the base addresses for .eh_frame encoded pointers, if | ||||
|   // possible. | ||||
|   byte_reader.SetCFIDataBase(section->sh_addr, cfi); | ||||
|   if (got_section) | ||||
|     byte_reader.SetDataBase(got_section->sh_addr); | ||||
|   if (text_section) | ||||
|     byte_reader.SetTextBase(text_section->sh_addr); | ||||
|  | ||||
|   dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, | ||||
|                                                        section_name); | ||||
|   dwarf2reader::CallFrameInfo parser(cfi, cfi_size, | ||||
|                                      &byte_reader, &handler, &dwarf_reporter, | ||||
|                                      eh_frame); | ||||
|   parser.Start(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| bool LoadELF(const string& obj_file, MmapWrapper* map_wrapper, | ||||
|              void** elf_header) { | ||||
|   int obj_fd = open(obj_file.c_str(), O_RDONLY); | ||||
|   if (obj_fd < 0) { | ||||
|     fprintf(stderr, "Failed to open ELF file '%s': %s\n", | ||||
|             obj_file.c_str(), strerror(errno)); | ||||
|     return false; | ||||
|   } | ||||
|   FDWrapper obj_fd_wrapper(obj_fd); | ||||
|   struct stat st; | ||||
|   if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { | ||||
|     fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", | ||||
|             obj_file.c_str(), strerror(errno)); | ||||
|     return false; | ||||
|   } | ||||
|   void *obj_base = mmap(NULL, st.st_size, | ||||
|                         PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); | ||||
|   if (obj_base == MAP_FAILED) { | ||||
|     fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", | ||||
|             obj_file.c_str(), strerror(errno)); | ||||
|     return false; | ||||
|   } | ||||
|   map_wrapper->set(obj_base, st.st_size); | ||||
|   *elf_header = obj_base; | ||||
|   if (!IsValidElf(*elf_header)) { | ||||
|     fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); | ||||
|     return false; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // Get the endianness of ELF_HEADER. If it's invalid, return false. | ||||
| template<typename ElfClass> | ||||
| bool ElfEndianness(const typename ElfClass::Ehdr* elf_header, | ||||
|                    bool* big_endian) { | ||||
|   if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) { | ||||
|     *big_endian = false; | ||||
|     return true; | ||||
|   } | ||||
|   if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) { | ||||
|     *big_endian = true; | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   fprintf(stderr, "bad data encoding in ELF header: %d\n", | ||||
|           elf_header->e_ident[EI_DATA]); | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| // Read the .gnu_debuglink and get the debug file name. If anything goes | ||||
| // wrong, return an empty string. | ||||
| template<typename ElfClass> | ||||
| string ReadDebugLink(const char* debuglink, | ||||
|                      size_t debuglink_size, | ||||
|                      const string& obj_file, | ||||
|                      const std::vector<string>& debug_dirs) { | ||||
|   size_t debuglink_len = strlen(debuglink) + 5;  // '\0' + CRC32. | ||||
|   debuglink_len = 4 * ((debuglink_len + 3) / 4);  // Round to nearest 4 bytes. | ||||
|  | ||||
|   // Sanity check. | ||||
|   if (debuglink_len != debuglink_size) { | ||||
|     fprintf(stderr, "Mismatched .gnu_debuglink string / section size: " | ||||
|             "%zx %zx\n", debuglink_len, debuglink_size); | ||||
|     return ""; | ||||
|   } | ||||
|  | ||||
|   bool found = false; | ||||
|   int debuglink_fd = -1; | ||||
|   string debuglink_path; | ||||
|   std::vector<string>::const_iterator it; | ||||
|   for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { | ||||
|     const string& debug_dir = *it; | ||||
|     debuglink_path = debug_dir + "/" + debuglink; | ||||
|     debuglink_fd = open(debuglink_path.c_str(), O_RDONLY); | ||||
|     if (debuglink_fd >= 0) { | ||||
|       found = true; | ||||
|       break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (!found) { | ||||
|     fprintf(stderr, "Failed to find debug ELF file for '%s' after trying:\n", | ||||
|             obj_file.c_str()); | ||||
|     for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { | ||||
|       const string debug_dir = *it; | ||||
|       fprintf(stderr, "  %s/%s\n", debug_dir.c_str(), debuglink); | ||||
|     } | ||||
|     return ""; | ||||
|   } | ||||
|  | ||||
|   FDWrapper debuglink_fd_wrapper(debuglink_fd); | ||||
|   // TODO(thestig) check the CRC-32 at the end of the .gnu_debuglink | ||||
|   // section. | ||||
|  | ||||
|   return debuglink_path; | ||||
| } | ||||
|  | ||||
| // | ||||
| // LoadSymbolsInfo | ||||
| // | ||||
| // Holds the state between the two calls to LoadSymbols() in case it's necessary | ||||
| // to follow the .gnu_debuglink section and load debug information from a | ||||
| // different file. | ||||
| // | ||||
| template<typename ElfClass> | ||||
| class LoadSymbolsInfo { | ||||
|  public: | ||||
|   typedef typename ElfClass::Addr Addr; | ||||
|  | ||||
|   explicit LoadSymbolsInfo(const std::vector<string>& dbg_dirs) : | ||||
|     debug_dirs_(dbg_dirs), | ||||
|     has_loading_addr_(false) {} | ||||
|  | ||||
|   // Keeps track of which sections have been loaded so sections don't | ||||
|   // accidentally get loaded twice from two different files. | ||||
|   void LoadedSection(const string §ion) { | ||||
|     if (loaded_sections_.count(section) == 0) { | ||||
|       loaded_sections_.insert(section); | ||||
|     } else { | ||||
|       fprintf(stderr, "Section %s has already been loaded.\n", | ||||
|               section.c_str()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // The ELF file and linked debug file are expected to have the same preferred | ||||
|   // loading address. | ||||
|   void set_loading_addr(Addr addr, const string &filename) { | ||||
|     if (!has_loading_addr_) { | ||||
|       loading_addr_ = addr; | ||||
|       loaded_file_ = filename; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (addr != loading_addr_) { | ||||
|       fprintf(stderr, | ||||
|               "ELF file '%s' and debug ELF file '%s' " | ||||
|               "have different load addresses.\n", | ||||
|               loaded_file_.c_str(), filename.c_str()); | ||||
|       assert(false); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Setters and getters | ||||
|   const std::vector<string>& debug_dirs() const { | ||||
|     return debug_dirs_; | ||||
|   } | ||||
|  | ||||
|   string debuglink_file() const { | ||||
|     return debuglink_file_; | ||||
|   } | ||||
|   void set_debuglink_file(string file) { | ||||
|     debuglink_file_ = file; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   const std::vector<string>& debug_dirs_; // Directories in which to | ||||
|                                           // search for the debug ELF file. | ||||
|  | ||||
|   string debuglink_file_;  // Full path to the debug ELF file. | ||||
|  | ||||
|   bool has_loading_addr_;  // Indicate if LOADING_ADDR_ is valid. | ||||
|  | ||||
|   Addr loading_addr_;  // Saves the preferred loading address from the | ||||
|                        // first call to LoadSymbols(). | ||||
|  | ||||
|   string loaded_file_;  // Name of the file loaded from the first call to | ||||
|                         // LoadSymbols(). | ||||
|  | ||||
|   std::set<string> loaded_sections_;  // Tracks the Loaded ELF sections | ||||
|                                       // between calls to LoadSymbols(). | ||||
| }; | ||||
|  | ||||
| template<typename ElfClass> | ||||
| bool LoadSymbols(const string& obj_file, | ||||
|                  const bool big_endian, | ||||
|                  const typename ElfClass::Ehdr* elf_header, | ||||
|                  const bool read_gnu_debug_link, | ||||
|                  LoadSymbolsInfo<ElfClass>* info, | ||||
|                  const DumpOptions& options, | ||||
|                  Module* module) { | ||||
|   typedef typename ElfClass::Addr Addr; | ||||
|   typedef typename ElfClass::Phdr Phdr; | ||||
|   typedef typename ElfClass::Shdr Shdr; | ||||
|   typedef typename ElfClass::Word Word; | ||||
|  | ||||
|   Addr loading_addr = GetLoadingAddress<ElfClass>( | ||||
|       GetOffset<ElfClass, Phdr>(elf_header, elf_header->e_phoff), | ||||
|       elf_header->e_phnum); | ||||
|   module->SetLoadAddress(loading_addr); | ||||
|   info->set_loading_addr(loading_addr, obj_file); | ||||
|  | ||||
|   Word debug_section_type =  | ||||
|       elf_header->e_machine == EM_MIPS ? SHT_MIPS_DWARF : SHT_PROGBITS; | ||||
|   const Shdr* sections = | ||||
|       GetOffset<ElfClass, Shdr>(elf_header, elf_header->e_shoff); | ||||
|   const Shdr* section_names = sections + elf_header->e_shstrndx; | ||||
|   const char* names = | ||||
|       GetOffset<ElfClass, char>(elf_header, section_names->sh_offset); | ||||
|   const char *names_end = names + section_names->sh_size; | ||||
|   bool found_debug_info_section = false; | ||||
|   bool found_usable_info = false; | ||||
|  | ||||
|   if (options.symbol_data != ONLY_CFI) { | ||||
| #ifndef NO_STABS_SUPPORT | ||||
|     // Look for STABS debugging information, and load it if present. | ||||
|     const Shdr* stab_section = | ||||
|       FindElfSectionByName<ElfClass>(".stab", SHT_PROGBITS, | ||||
|                                      sections, names, names_end, | ||||
|                                      elf_header->e_shnum); | ||||
|     if (stab_section) { | ||||
|       const Shdr* stabstr_section = stab_section->sh_link + sections; | ||||
|       if (stabstr_section) { | ||||
|         found_debug_info_section = true; | ||||
|         found_usable_info = true; | ||||
|         info->LoadedSection(".stab"); | ||||
|         if (!LoadStabs<ElfClass>(elf_header, stab_section, stabstr_section, | ||||
|                                  big_endian, module)) { | ||||
|           fprintf(stderr, "%s: \".stab\" section found, but failed to load" | ||||
|                   " STABS debugging information\n", obj_file.c_str()); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
| #endif  // NO_STABS_SUPPORT | ||||
|  | ||||
|     // Look for DWARF debugging information, and load it if present. | ||||
|     const Shdr* dwarf_section = | ||||
|       FindElfSectionByName<ElfClass>(".debug_info", debug_section_type, | ||||
|                                      sections, names, names_end, | ||||
|                                      elf_header->e_shnum); | ||||
|     if (dwarf_section) { | ||||
|       found_debug_info_section = true; | ||||
|       found_usable_info = true; | ||||
|       info->LoadedSection(".debug_info"); | ||||
|       if (!LoadDwarf<ElfClass>(obj_file, elf_header, big_endian, | ||||
|                                options.handle_inter_cu_refs, module)) { | ||||
|         fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " | ||||
|                 "DWARF debugging information\n", obj_file.c_str()); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (options.symbol_data != NO_CFI) { | ||||
|     // Dwarf Call Frame Information (CFI) is actually independent from | ||||
|     // the other DWARF debugging information, and can be used alone. | ||||
|     const Shdr* dwarf_cfi_section = | ||||
|         FindElfSectionByName<ElfClass>(".debug_frame", debug_section_type, | ||||
|                                        sections, names, names_end, | ||||
|                                        elf_header->e_shnum); | ||||
|     if (dwarf_cfi_section) { | ||||
|       // Ignore the return value of this function; even without call frame | ||||
|       // information, the other debugging information could be perfectly | ||||
|       // useful. | ||||
|       info->LoadedSection(".debug_frame"); | ||||
|       bool result = | ||||
|           LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".debug_frame", | ||||
|                                  dwarf_cfi_section, false, 0, 0, big_endian, | ||||
|                                  module); | ||||
|       found_usable_info = found_usable_info || result; | ||||
|     } | ||||
|  | ||||
|     // Linux C++ exception handling information can also provide | ||||
|     // unwinding data. | ||||
|     const Shdr* eh_frame_section = | ||||
|         FindElfSectionByName<ElfClass>(".eh_frame", SHT_PROGBITS, | ||||
|                                        sections, names, names_end, | ||||
|                                        elf_header->e_shnum); | ||||
|     if (eh_frame_section) { | ||||
|       // Pointers in .eh_frame data may be relative to the base addresses of | ||||
|       // certain sections. Provide those sections if present. | ||||
|       const Shdr* got_section = | ||||
|           FindElfSectionByName<ElfClass>(".got", SHT_PROGBITS, | ||||
|                                          sections, names, names_end, | ||||
|                                          elf_header->e_shnum); | ||||
|       const Shdr* text_section = | ||||
|           FindElfSectionByName<ElfClass>(".text", SHT_PROGBITS, | ||||
|                                          sections, names, names_end, | ||||
|                                          elf_header->e_shnum); | ||||
|       info->LoadedSection(".eh_frame"); | ||||
|       // As above, ignore the return value of this function. | ||||
|       bool result = | ||||
|           LoadDwarfCFI<ElfClass>(obj_file, elf_header, ".eh_frame", | ||||
|                                  eh_frame_section, true, | ||||
|                                  got_section, text_section, big_endian, module); | ||||
|       found_usable_info = found_usable_info || result; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (!found_debug_info_section) { | ||||
|     fprintf(stderr, "%s: file contains no debugging information" | ||||
|             " (no \".stab\" or \".debug_info\" sections)\n", | ||||
|             obj_file.c_str()); | ||||
|  | ||||
|     // Failed, but maybe there's a .gnu_debuglink section? | ||||
|     if (read_gnu_debug_link) { | ||||
|       const Shdr* gnu_debuglink_section | ||||
|           = FindElfSectionByName<ElfClass>(".gnu_debuglink", SHT_PROGBITS, | ||||
|                                            sections, names, | ||||
|                                            names_end, elf_header->e_shnum); | ||||
|       if (gnu_debuglink_section) { | ||||
|         if (!info->debug_dirs().empty()) { | ||||
|           const char* debuglink_contents = | ||||
|               GetOffset<ElfClass, char>(elf_header, | ||||
|                                         gnu_debuglink_section->sh_offset); | ||||
|           string debuglink_file | ||||
|               = ReadDebugLink<ElfClass>(debuglink_contents, | ||||
|                                         gnu_debuglink_section->sh_size, | ||||
|                                         obj_file, info->debug_dirs()); | ||||
|           info->set_debuglink_file(debuglink_file); | ||||
|         } else { | ||||
|           fprintf(stderr, ".gnu_debuglink section found in '%s', " | ||||
|                   "but no debug path specified.\n", obj_file.c_str()); | ||||
|         } | ||||
|       } else { | ||||
|         fprintf(stderr, "%s does not contain a .gnu_debuglink section.\n", | ||||
|                 obj_file.c_str()); | ||||
|       } | ||||
|     } else { | ||||
|       if (options.symbol_data != ONLY_CFI) { | ||||
|         // The caller doesn't want to consult .gnu_debuglink. | ||||
|         // See if there are export symbols available. | ||||
|         const Shdr* dynsym_section = | ||||
|           FindElfSectionByName<ElfClass>(".dynsym", SHT_DYNSYM, | ||||
|                                          sections, names, names_end, | ||||
|                                          elf_header->e_shnum); | ||||
|         const Shdr* dynstr_section = | ||||
|           FindElfSectionByName<ElfClass>(".dynstr", SHT_STRTAB, | ||||
|                                          sections, names, names_end, | ||||
|                                          elf_header->e_shnum); | ||||
|         if (dynsym_section && dynstr_section) { | ||||
|           info->LoadedSection(".dynsym"); | ||||
|  | ||||
|           const uint8_t* dynsyms = | ||||
|               GetOffset<ElfClass, uint8_t>(elf_header, | ||||
|                                            dynsym_section->sh_offset); | ||||
|           const uint8_t* dynstrs = | ||||
|               GetOffset<ElfClass, uint8_t>(elf_header, | ||||
|                                            dynstr_section->sh_offset); | ||||
|           bool result = | ||||
|               ELFSymbolsToModule(dynsyms, | ||||
|                                  dynsym_section->sh_size, | ||||
|                                  dynstrs, | ||||
|                                  dynstr_section->sh_size, | ||||
|                                  big_endian, | ||||
|                                  ElfClass::kAddrSize, | ||||
|                                  module); | ||||
|           found_usable_info = found_usable_info || result; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       // Return true if some usable information was found, since | ||||
|       // the caller doesn't want to use .gnu_debuglink. | ||||
|       return found_usable_info; | ||||
|     } | ||||
|  | ||||
|     // No debug info was found, let the user try again with .gnu_debuglink | ||||
|     // if present. | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // Return the breakpad symbol file identifier for the architecture of | ||||
| // ELF_HEADER. | ||||
| template<typename ElfClass> | ||||
| const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) { | ||||
|   typedef typename ElfClass::Half Half; | ||||
|   Half arch = elf_header->e_machine; | ||||
|   switch (arch) { | ||||
|     case EM_386:        return "x86"; | ||||
|     case EM_ARM:        return "arm"; | ||||
|     case EM_MIPS:       return "mips"; | ||||
|     case EM_PPC64:      return "ppc64"; | ||||
|     case EM_PPC:        return "ppc"; | ||||
|     case EM_S390:       return "s390"; | ||||
|     case EM_SPARC:      return "sparc"; | ||||
|     case EM_SPARCV9:    return "sparcv9"; | ||||
|     case EM_X86_64:     return "x86_64"; | ||||
|     default: return NULL; | ||||
|   } | ||||
| } | ||||
|  | ||||
| // Format the Elf file identifier in IDENTIFIER as a UUID with the | ||||
| // dashes removed. | ||||
| string FormatIdentifier(unsigned char identifier[16]) { | ||||
|   char identifier_str[40]; | ||||
|   google_breakpad::FileID::ConvertIdentifierToString( | ||||
|       identifier, | ||||
|       identifier_str, | ||||
|       sizeof(identifier_str)); | ||||
|   string id_no_dash; | ||||
|   for (int i = 0; identifier_str[i] != '\0'; ++i) | ||||
|     if (identifier_str[i] != '-') | ||||
|       id_no_dash += identifier_str[i]; | ||||
|   // Add an extra "0" by the end.  PDB files on Windows have an 'age' | ||||
|   // number appended to the end of the file identifier; this isn't | ||||
|   // really used or necessary on other platforms, but be consistent. | ||||
|   id_no_dash += '0'; | ||||
|   return id_no_dash; | ||||
| } | ||||
|  | ||||
| // Return the non-directory portion of FILENAME: the portion after the | ||||
| // last slash, or the whole filename if there are no slashes. | ||||
| string BaseFileName(const string &filename) { | ||||
|   // Lots of copies!  basename's behavior is less than ideal. | ||||
|   char *c_filename = strdup(filename.c_str()); | ||||
|   string base = basename(c_filename); | ||||
|   free(c_filename); | ||||
|   return base; | ||||
| } | ||||
|  | ||||
| template<typename ElfClass> | ||||
| bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, | ||||
|                              const string& obj_filename, | ||||
|                              const std::vector<string>& debug_dirs, | ||||
|                              const DumpOptions& options, | ||||
|                              Module** out_module) { | ||||
|   typedef typename ElfClass::Ehdr Ehdr; | ||||
|   typedef typename ElfClass::Shdr Shdr; | ||||
|  | ||||
|   *out_module = NULL; | ||||
|  | ||||
|   unsigned char identifier[16]; | ||||
|   if (!google_breakpad::FileID::ElfFileIdentifierFromMappedFile(elf_header, | ||||
|                                                                 identifier)) { | ||||
|     fprintf(stderr, "%s: unable to generate file identifier\n", | ||||
|             obj_filename.c_str()); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   const char *architecture = ElfArchitecture<ElfClass>(elf_header); | ||||
|   if (!architecture) { | ||||
|     fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", | ||||
|             obj_filename.c_str(), elf_header->e_machine); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   // Figure out what endianness this file is. | ||||
|   bool big_endian; | ||||
|   if (!ElfEndianness<ElfClass>(elf_header, &big_endian)) | ||||
|     return false; | ||||
|  | ||||
|   string name = BaseFileName(obj_filename); | ||||
|   string os = "Linux"; | ||||
|   string id = FormatIdentifier(identifier); | ||||
|  | ||||
|   LoadSymbolsInfo<ElfClass> info(debug_dirs); | ||||
|   scoped_ptr<Module> module(new Module(name, os, architecture, id)); | ||||
|   if (!LoadSymbols<ElfClass>(obj_filename, big_endian, elf_header, | ||||
|                              !debug_dirs.empty(), &info, | ||||
|                              options, module.get())) { | ||||
|     const string debuglink_file = info.debuglink_file(); | ||||
|     if (debuglink_file.empty()) | ||||
|       return false; | ||||
|  | ||||
|     // Load debuglink ELF file. | ||||
|     fprintf(stderr, "Found debugging info in %s\n", debuglink_file.c_str()); | ||||
|     MmapWrapper debug_map_wrapper; | ||||
|     Ehdr* debug_elf_header = NULL; | ||||
|     if (!LoadELF(debuglink_file, &debug_map_wrapper, | ||||
|                  reinterpret_cast<void**>(&debug_elf_header))) | ||||
|       return false; | ||||
|     // Sanity checks to make sure everything matches up. | ||||
|     const char *debug_architecture = | ||||
|         ElfArchitecture<ElfClass>(debug_elf_header); | ||||
|     if (!debug_architecture) { | ||||
|       fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n", | ||||
|               debuglink_file.c_str(), debug_elf_header->e_machine); | ||||
|       return false; | ||||
|     } | ||||
|     if (strcmp(architecture, debug_architecture)) { | ||||
|       fprintf(stderr, "%s with ELF machine architecture %s does not match " | ||||
|               "%s with ELF architecture %s\n", | ||||
|               debuglink_file.c_str(), debug_architecture, | ||||
|               obj_filename.c_str(), architecture); | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     bool debug_big_endian; | ||||
|     if (!ElfEndianness<ElfClass>(debug_elf_header, &debug_big_endian)) | ||||
|       return false; | ||||
|     if (debug_big_endian != big_endian) { | ||||
|       fprintf(stderr, "%s and %s does not match in endianness\n", | ||||
|               obj_filename.c_str(), debuglink_file.c_str()); | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     if (!LoadSymbols<ElfClass>(debuglink_file, debug_big_endian, | ||||
|                                debug_elf_header, false, &info, | ||||
|                                options, module.get())) { | ||||
|       return false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   *out_module = module.release(); | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // Not explicitly exported, but not static so it can be used in unit tests. | ||||
| bool ReadSymbolDataInternal(const uint8_t* obj_file, | ||||
|                             const string& obj_filename, | ||||
|                             const std::vector<string>& debug_dirs, | ||||
|                             const DumpOptions& options, | ||||
|                             Module** module) { | ||||
|   if (!IsValidElf(obj_file)) { | ||||
|     fprintf(stderr, "Not a valid ELF file: %s\n", obj_filename.c_str()); | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   int elfclass = ElfClass(obj_file); | ||||
|   if (elfclass == ELFCLASS32) { | ||||
|     return ReadSymbolDataElfClass<ElfClass32>( | ||||
|         reinterpret_cast<const Elf32_Ehdr*>(obj_file), obj_filename, debug_dirs, | ||||
|         options, module); | ||||
|   } | ||||
|   if (elfclass == ELFCLASS64) { | ||||
|     return ReadSymbolDataElfClass<ElfClass64>( | ||||
|         reinterpret_cast<const Elf64_Ehdr*>(obj_file), obj_filename, debug_dirs, | ||||
|         options, module); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool WriteSymbolFile(const string &obj_file, | ||||
|                      const std::vector<string>& debug_dirs, | ||||
|                      const DumpOptions& options, | ||||
|                      std::ostream &sym_stream) { | ||||
|   Module* module; | ||||
|   if (!ReadSymbolData(obj_file, debug_dirs, options, &module)) | ||||
|     return false; | ||||
|  | ||||
|   bool result = module->Write(sym_stream, options.symbol_data); | ||||
|   delete module; | ||||
|   return result; | ||||
| } | ||||
|  | ||||
| bool ReadSymbolData(const string& obj_file, | ||||
|                     const std::vector<string>& debug_dirs, | ||||
|                     const DumpOptions& options, | ||||
|                     Module** module) { | ||||
|   MmapWrapper map_wrapper; | ||||
|   void* elf_header = NULL; | ||||
|   if (!LoadELF(obj_file, &map_wrapper, &elf_header)) | ||||
|     return false; | ||||
|  | ||||
|   return ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(elf_header), | ||||
|                                 obj_file, debug_dirs, options, module); | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										80
									
								
								sdk/breakpad/common/linux/dump_symbols.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								sdk/breakpad/common/linux/dump_symbols.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2011, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // dump_symbols.h: Read debugging information from an ELF file, and write | ||||
| // it out as a Breakpad symbol file. | ||||
|  | ||||
| #ifndef COMMON_LINUX_DUMP_SYMBOLS_H__ | ||||
| #define COMMON_LINUX_DUMP_SYMBOLS_H__ | ||||
|  | ||||
| #include <iostream> | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/symbol_data.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class Module; | ||||
|  | ||||
| struct DumpOptions { | ||||
|   DumpOptions(SymbolData symbol_data, bool handle_inter_cu_refs) | ||||
|       : symbol_data(symbol_data), | ||||
|         handle_inter_cu_refs(handle_inter_cu_refs) { | ||||
|   } | ||||
|  | ||||
|   SymbolData symbol_data; | ||||
|   bool handle_inter_cu_refs; | ||||
| }; | ||||
|  | ||||
| // Find all the debugging information in OBJ_FILE, an ELF executable | ||||
| // or shared library, and write it to SYM_STREAM in the Breakpad symbol | ||||
| // file format. | ||||
| // If OBJ_FILE has been stripped but contains a .gnu_debuglink section, | ||||
| // then look for the debug file in DEBUG_DIRS. | ||||
| // SYMBOL_DATA allows limiting the type of symbol data written. | ||||
| bool WriteSymbolFile(const string &obj_file, | ||||
|                      const std::vector<string>& debug_dirs, | ||||
|                      const DumpOptions& options, | ||||
|                      std::ostream &sym_stream); | ||||
|  | ||||
| // As above, but simply return the debugging information in MODULE | ||||
| // instead of writing it to a stream. The caller owns the resulting | ||||
| // Module object and must delete it when finished. | ||||
| bool ReadSymbolData(const string& obj_file, | ||||
|                     const std::vector<string>& debug_dirs, | ||||
|                     const DumpOptions& options, | ||||
|                     Module** module); | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_LINUX_DUMP_SYMBOLS_H__ | ||||
							
								
								
									
										172
									
								
								sdk/breakpad/common/linux/dump_symbols_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								sdk/breakpad/common/linux/dump_symbols_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| // Copyright (c) 2011 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Ted Mielczarek <ted.mielczarek@gmail.com> | ||||
|  | ||||
| // dump_symbols_unittest.cc: | ||||
| // Unittests for google_breakpad::DumpSymbols | ||||
|  | ||||
| #include <elf.h> | ||||
| #include <link.h> | ||||
| #include <stdio.h> | ||||
|  | ||||
| #include <sstream> | ||||
| #include <vector> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/linux/dump_symbols.h" | ||||
| #include "common/linux/synth_elf.h" | ||||
| #include "common/module.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| bool ReadSymbolDataInternal(const uint8_t* obj_file, | ||||
|                             const string& obj_filename, | ||||
|                             const std::vector<string>& debug_dir, | ||||
|                             const DumpOptions& options, | ||||
|                             Module** module); | ||||
|  | ||||
| using google_breakpad::synth_elf::ELF; | ||||
| using google_breakpad::synth_elf::StringTable; | ||||
| using google_breakpad::synth_elf::SymbolTable; | ||||
| using google_breakpad::test_assembler::kLittleEndian; | ||||
| using google_breakpad::test_assembler::Section; | ||||
| using std::stringstream; | ||||
| using std::vector; | ||||
| using ::testing::Test; | ||||
|  | ||||
| class DumpSymbols : public Test { | ||||
|  public: | ||||
|   void GetElfContents(ELF& elf) { | ||||
|     string contents; | ||||
|     ASSERT_TRUE(elf.GetContents(&contents)); | ||||
|     ASSERT_LT(0U, contents.size()); | ||||
|  | ||||
|     elfdata_v.clear(); | ||||
|     elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); | ||||
|     elfdata = &elfdata_v[0]; | ||||
|   } | ||||
|  | ||||
|   vector<uint8_t> elfdata_v; | ||||
|   uint8_t* elfdata; | ||||
| }; | ||||
|  | ||||
| TEST_F(DumpSymbols, Invalid) { | ||||
|   Elf32_Ehdr header; | ||||
|   memset(&header, 0, sizeof(header)); | ||||
|   Module* module; | ||||
|   DumpOptions options(ALL_SYMBOL_DATA, true); | ||||
|   EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(&header), | ||||
|                                       "foo", | ||||
|                                       vector<string>(), | ||||
|                                       options, | ||||
|                                       &module)); | ||||
| } | ||||
|  | ||||
| TEST_F(DumpSymbols, SimplePublic32) { | ||||
|   ELF elf(EM_386, ELFCLASS32, kLittleEndian); | ||||
|   // Zero out text section for simplicity. | ||||
|   Section text(kLittleEndian); | ||||
|   text.Append(4096, 0); | ||||
|   elf.AddSection(".text", text, SHT_PROGBITS); | ||||
|  | ||||
|   // Add a public symbol. | ||||
|   StringTable table(kLittleEndian); | ||||
|   SymbolTable syms(kLittleEndian, 4, table); | ||||
|   syms.AddSymbol("superfunc", (uint32_t)0x1000, (uint32_t)0x10, | ||||
|                  ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|                  SHN_UNDEF + 1); | ||||
|   int index = elf.AddSection(".dynstr", table, SHT_STRTAB); | ||||
|   elf.AddSection(".dynsym", syms, | ||||
|                  SHT_DYNSYM,          // type | ||||
|                  SHF_ALLOC,           // flags | ||||
|                  0,                   // addr | ||||
|                  index,               // link | ||||
|                  sizeof(Elf32_Sym));  // entsize | ||||
|  | ||||
|   elf.Finish(); | ||||
|   GetElfContents(elf); | ||||
|  | ||||
|   Module* module; | ||||
|   DumpOptions options(ALL_SYMBOL_DATA, true); | ||||
|   EXPECT_TRUE(ReadSymbolDataInternal(elfdata, | ||||
|                                      "foo", | ||||
|                                      vector<string>(), | ||||
|                                      options, | ||||
|                                      &module)); | ||||
|  | ||||
|   stringstream s; | ||||
|   module->Write(s, ALL_SYMBOL_DATA); | ||||
|   EXPECT_EQ("MODULE Linux x86 000000000000000000000000000000000 foo\n" | ||||
|             "PUBLIC 1000 0 superfunc\n", | ||||
|             s.str()); | ||||
|   delete module; | ||||
| } | ||||
|  | ||||
| TEST_F(DumpSymbols, SimplePublic64) { | ||||
|   ELF elf(EM_X86_64, ELFCLASS64, kLittleEndian); | ||||
|   // Zero out text section for simplicity. | ||||
|   Section text(kLittleEndian); | ||||
|   text.Append(4096, 0); | ||||
|   elf.AddSection(".text", text, SHT_PROGBITS); | ||||
|  | ||||
|   // Add a public symbol. | ||||
|   StringTable table(kLittleEndian); | ||||
|   SymbolTable syms(kLittleEndian, 8, table); | ||||
|   syms.AddSymbol("superfunc", (uint64_t)0x1000, (uint64_t)0x10, | ||||
|                  ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|                  SHN_UNDEF + 1); | ||||
|   int index = elf.AddSection(".dynstr", table, SHT_STRTAB); | ||||
|   elf.AddSection(".dynsym", syms, | ||||
|                  SHT_DYNSYM,          // type | ||||
|                  SHF_ALLOC,           // flags | ||||
|                  0,                   // addr | ||||
|                  index,               // link | ||||
|                  sizeof(Elf64_Sym));  // entsize | ||||
|  | ||||
|   elf.Finish(); | ||||
|   GetElfContents(elf); | ||||
|  | ||||
|   Module* module; | ||||
|   DumpOptions options(ALL_SYMBOL_DATA, true); | ||||
|   EXPECT_TRUE(ReadSymbolDataInternal(elfdata, | ||||
|                                      "foo", | ||||
|                                      vector<string>(), | ||||
|                                      options, | ||||
|                                      &module)); | ||||
|  | ||||
|   stringstream s; | ||||
|   module->Write(s, ALL_SYMBOL_DATA); | ||||
|   EXPECT_EQ("MODULE Linux x86_64 000000000000000000000000000000000 foo\n" | ||||
|             "PUBLIC 1000 0 superfunc\n", | ||||
|             s.str()); | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										58
									
								
								sdk/breakpad/common/linux/eintr_wrapper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								sdk/breakpad/common/linux/eintr_wrapper.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| // Copyright (c) 2010 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef COMMON_LINUX_EINTR_WRAPPER_H_ | ||||
| #define COMMON_LINUX_EINTR_WRAPPER_H_ | ||||
|  | ||||
| #include <errno.h> | ||||
|  | ||||
| // This provides a wrapper around system calls which may be interrupted by a | ||||
| // signal and return EINTR. See man 7 signal. | ||||
| // | ||||
|  | ||||
| #define HANDLE_EINTR(x) ({ \ | ||||
|   typeof(x) eintr_wrapper_result; \ | ||||
|   do { \ | ||||
|     eintr_wrapper_result = (x); \ | ||||
|   } while (eintr_wrapper_result == -1 && errno == EINTR); \ | ||||
|   eintr_wrapper_result; \ | ||||
| }) | ||||
|  | ||||
| #define IGNORE_EINTR(x) ({ \ | ||||
|   typeof(x) eintr_wrapper_result; \ | ||||
|   do { \ | ||||
|     eintr_wrapper_result = (x); \ | ||||
|     if (eintr_wrapper_result == -1 && errno == EINTR) { \ | ||||
|       eintr_wrapper_result = 0; \ | ||||
|     } \ | ||||
|   } while (0); \ | ||||
|   eintr_wrapper_result; \ | ||||
| }) | ||||
|  | ||||
| #endif  // COMMON_LINUX_EINTR_WRAPPER_H_ | ||||
							
								
								
									
										179
									
								
								sdk/breakpad/common/linux/elf_core_dump.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								sdk/breakpad/common/linux/elf_core_dump.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| // Copyright (c) 2011, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // elf_core_dump.cc: Implement google_breakpad::ElfCoreDump. | ||||
| // See elf_core_dump.h for details. | ||||
|  | ||||
| #include "common/linux/elf_core_dump.h" | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include <string.h> | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // Implementation of ElfCoreDump::Note. | ||||
|  | ||||
| ElfCoreDump::Note::Note() {} | ||||
|  | ||||
| ElfCoreDump::Note::Note(const MemoryRange& content) : content_(content) {} | ||||
|  | ||||
| bool ElfCoreDump::Note::IsValid() const { | ||||
|   return GetHeader() != NULL; | ||||
| } | ||||
|  | ||||
| const ElfCoreDump::Nhdr* ElfCoreDump::Note::GetHeader() const { | ||||
|   return content_.GetData<Nhdr>(0); | ||||
| } | ||||
|  | ||||
| ElfCoreDump::Word ElfCoreDump::Note::GetType() const { | ||||
|   const Nhdr* header = GetHeader(); | ||||
|   // 0 is not being used as a NOTE type. | ||||
|   return header ? header->n_type : 0; | ||||
| } | ||||
|  | ||||
| MemoryRange ElfCoreDump::Note::GetName() const { | ||||
|   const Nhdr* header = GetHeader(); | ||||
|   if (header) { | ||||
|       return content_.Subrange(sizeof(Nhdr), header->n_namesz); | ||||
|   } | ||||
|   return MemoryRange(); | ||||
| } | ||||
|  | ||||
| MemoryRange ElfCoreDump::Note::GetDescription() const { | ||||
|   const Nhdr* header = GetHeader(); | ||||
|   if (header) { | ||||
|     return content_.Subrange(AlignedSize(sizeof(Nhdr) + header->n_namesz), | ||||
|                              header->n_descsz); | ||||
|   } | ||||
|   return MemoryRange(); | ||||
| } | ||||
|  | ||||
| ElfCoreDump::Note ElfCoreDump::Note::GetNextNote() const { | ||||
|   MemoryRange next_content; | ||||
|   const Nhdr* header = GetHeader(); | ||||
|   if (header) { | ||||
|     size_t next_offset = AlignedSize(sizeof(Nhdr) + header->n_namesz); | ||||
|     next_offset = AlignedSize(next_offset + header->n_descsz); | ||||
|     next_content = | ||||
|         content_.Subrange(next_offset, content_.length() - next_offset); | ||||
|   } | ||||
|   return Note(next_content); | ||||
| } | ||||
|  | ||||
| // static | ||||
| size_t ElfCoreDump::Note::AlignedSize(size_t size) { | ||||
|   size_t mask = sizeof(Word) - 1; | ||||
|   return (size + mask) & ~mask; | ||||
| } | ||||
|  | ||||
|  | ||||
| // Implementation of ElfCoreDump. | ||||
|  | ||||
| ElfCoreDump::ElfCoreDump() {} | ||||
|  | ||||
| ElfCoreDump::ElfCoreDump(const MemoryRange& content) | ||||
|     : content_(content) { | ||||
| } | ||||
|  | ||||
| void ElfCoreDump::SetContent(const MemoryRange& content) { | ||||
|   content_ = content; | ||||
| } | ||||
|  | ||||
| bool ElfCoreDump::IsValid() const { | ||||
|   const Ehdr* header = GetHeader(); | ||||
|   return (header && | ||||
|           header->e_ident[0] == ELFMAG0 && | ||||
|           header->e_ident[1] == ELFMAG1 && | ||||
|           header->e_ident[2] == ELFMAG2 && | ||||
|           header->e_ident[3] == ELFMAG3 && | ||||
|           header->e_ident[4] == kClass && | ||||
|           header->e_version == EV_CURRENT && | ||||
|           header->e_type == ET_CORE); | ||||
| } | ||||
|  | ||||
| const ElfCoreDump::Ehdr* ElfCoreDump::GetHeader() const { | ||||
|   return content_.GetData<Ehdr>(0); | ||||
| } | ||||
|  | ||||
| const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const { | ||||
|   const Ehdr* header = GetHeader(); | ||||
|   if (header) { | ||||
|     return reinterpret_cast<const Phdr*>(content_.GetArrayElement( | ||||
|         header->e_phoff, header->e_phentsize, index)); | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| const ElfCoreDump::Phdr* ElfCoreDump::GetFirstProgramHeaderOfType( | ||||
|     Word type) const { | ||||
|   for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { | ||||
|     const Phdr* program = GetProgramHeader(i); | ||||
|     if (program->p_type == type) { | ||||
|       return program; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| unsigned ElfCoreDump::GetProgramHeaderCount() const { | ||||
|   const Ehdr* header = GetHeader(); | ||||
|   return header ? header->e_phnum : 0; | ||||
| } | ||||
|  | ||||
| bool ElfCoreDump::CopyData(void* buffer, Addr virtual_address, size_t length) { | ||||
|   for (unsigned i = 0, n = GetProgramHeaderCount(); i < n; ++i) { | ||||
|     const Phdr* program = GetProgramHeader(i); | ||||
|     if (program->p_type != PT_LOAD) | ||||
|       continue; | ||||
|  | ||||
|     size_t offset_in_segment = virtual_address - program->p_vaddr; | ||||
|     if (virtual_address >= program->p_vaddr && | ||||
|         offset_in_segment < program->p_filesz) { | ||||
|       const void* data = | ||||
|           content_.GetData(program->p_offset + offset_in_segment, length); | ||||
|       if (data) { | ||||
|         memcpy(buffer, data, length); | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| ElfCoreDump::Note ElfCoreDump::GetFirstNote() const { | ||||
|   MemoryRange note_content; | ||||
|   const Phdr* program_header = GetFirstProgramHeaderOfType(PT_NOTE); | ||||
|   if (program_header) { | ||||
|     note_content = content_.Subrange(program_header->p_offset, | ||||
|                                      program_header->p_filesz); | ||||
|   } | ||||
|   return Note(note_content); | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										148
									
								
								sdk/breakpad/common/linux/elf_core_dump.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								sdk/breakpad/common/linux/elf_core_dump.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,148 @@ | ||||
| // Copyright (c) 2011, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // elf_core_dump.h: Define the google_breakpad::ElfCoreDump class, which | ||||
| // encapsulates an ELF core dump file mapped into memory. | ||||
|  | ||||
| #ifndef COMMON_LINUX_ELF_CORE_DUMP_H_ | ||||
| #define COMMON_LINUX_ELF_CORE_DUMP_H_ | ||||
|  | ||||
| #include <elf.h> | ||||
| #include <link.h> | ||||
| #include <stddef.h> | ||||
|  | ||||
| #include "common/memory_range.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // A class encapsulating an ELF core dump file mapped into memory, which | ||||
| // provides methods for accessing program headers and the note section. | ||||
| class ElfCoreDump { | ||||
|  public: | ||||
|   // ELF types based on the value of __WORDSIZE. | ||||
|   typedef ElfW(Ehdr) Ehdr; | ||||
|   typedef ElfW(Nhdr) Nhdr; | ||||
|   typedef ElfW(Phdr) Phdr; | ||||
|   typedef ElfW(Word) Word; | ||||
|   typedef ElfW(Addr) Addr; | ||||
| #if __WORDSIZE == 32 | ||||
|   static const int kClass = ELFCLASS32; | ||||
| #elif __WORDSIZE == 64 | ||||
|   static const int kClass = ELFCLASS64; | ||||
| #else | ||||
| #error "Unsupported __WORDSIZE for ElfCoreDump." | ||||
| #endif | ||||
|  | ||||
|   // A class encapsulating the note content in a core dump, which provides | ||||
|   // methods for accessing the name and description of a note. | ||||
|   class Note { | ||||
|    public: | ||||
|     Note(); | ||||
|  | ||||
|     // Constructor that takes the note content from |content|. | ||||
|     explicit Note(const MemoryRange& content); | ||||
|  | ||||
|     // Returns true if this note is valid, i,e. a note header is found in | ||||
|     // |content_|, or false otherwise. | ||||
|     bool IsValid() const; | ||||
|  | ||||
|     // Returns the note header, or NULL if no note header is found in | ||||
|     // |content_|. | ||||
|     const Nhdr* GetHeader() const; | ||||
|  | ||||
|     // Returns the note type, or 0 if no note header is found in |content_|. | ||||
|     Word GetType() const; | ||||
|  | ||||
|     // Returns a memory range covering the note name, or an empty range | ||||
|     // if no valid note name is found in |content_|. | ||||
|     MemoryRange GetName() const; | ||||
|  | ||||
|     // Returns a memory range covering the note description, or an empty | ||||
|     // range if no valid note description is found in |content_|. | ||||
|     MemoryRange GetDescription() const; | ||||
|  | ||||
|     // Returns the note following this note, or an empty note if no valid | ||||
|     // note is found after this note. | ||||
|     Note GetNextNote() const; | ||||
|  | ||||
|    private: | ||||
|     // Returns the size in bytes round up to the word alignment, specified | ||||
|     // for the note section, of a given size in bytes. | ||||
|     static size_t AlignedSize(size_t size); | ||||
|  | ||||
|     // Note content. | ||||
|     MemoryRange content_; | ||||
|   }; | ||||
|  | ||||
|   ElfCoreDump(); | ||||
|  | ||||
|   // Constructor that takes the core dump content from |content|. | ||||
|   explicit ElfCoreDump(const MemoryRange& content); | ||||
|  | ||||
|   // Sets the core dump content to |content|. | ||||
|   void SetContent(const MemoryRange& content); | ||||
|  | ||||
|   // Returns true if a valid ELF header in the core dump, or false otherwise. | ||||
|   bool IsValid() const; | ||||
|  | ||||
|   // Returns the ELF header in the core dump, or NULL if no ELF header | ||||
|   // is found in |content_|. | ||||
|   const Ehdr* GetHeader() const; | ||||
|  | ||||
|   // Returns the |index|-th program header in the core dump, or NULL if no | ||||
|   // ELF header is found in |content_| or |index| is out of bounds. | ||||
|   const Phdr* GetProgramHeader(unsigned index) const; | ||||
|  | ||||
|   // Returns the first program header of |type| in the core dump, or NULL if | ||||
|   // no ELF header is found in |content_| or no program header of |type| is | ||||
|   // found. | ||||
|   const Phdr* GetFirstProgramHeaderOfType(Word type) const; | ||||
|  | ||||
|   // Returns the number of program headers in the core dump, or 0 if no | ||||
|   // ELF header is found in |content_|. | ||||
|   unsigned GetProgramHeaderCount() const; | ||||
|  | ||||
|   // Copies |length| bytes of data starting at |virtual_address| in the core | ||||
|   // dump to |buffer|. |buffer| should be a valid pointer to a buffer of at | ||||
|   // least |length| bytes. Returns true if the data to be copied is found in | ||||
|   // the core dump, or false otherwise. | ||||
|   bool CopyData(void* buffer, Addr virtual_address, size_t length); | ||||
|  | ||||
|   // Returns the first note found in the note section of the core dump, or | ||||
|   // an empty note if no note is found. | ||||
|   Note GetFirstNote() const; | ||||
|  | ||||
|  private: | ||||
|   // Core dump content. | ||||
|   MemoryRange content_; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_LINUX_ELF_CORE_DUMP_H_ | ||||
							
								
								
									
										249
									
								
								sdk/breakpad/common/linux/elf_core_dump_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								sdk/breakpad/common/linux/elf_core_dump_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,249 @@ | ||||
| // Copyright (c) 2011, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // elf_core_dump_unittest.cc: Unit tests for google_breakpad::ElfCoreDump. | ||||
|  | ||||
| #include <sys/procfs.h> | ||||
|  | ||||
| #include <set> | ||||
| #include <string> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/linux/elf_core_dump.h" | ||||
| #include "common/linux/memory_mapped_file.h" | ||||
| #include "common/tests/file_utils.h" | ||||
| #include "common/linux/tests/crash_generator.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using google_breakpad::AutoTempDir; | ||||
| using google_breakpad::CrashGenerator; | ||||
| using google_breakpad::ElfCoreDump; | ||||
| using google_breakpad::MemoryMappedFile; | ||||
| using google_breakpad::MemoryRange; | ||||
| using google_breakpad::WriteFile; | ||||
| using std::set; | ||||
|  | ||||
| TEST(ElfCoreDumpTest, DefaultConstructor) { | ||||
|   ElfCoreDump core; | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|   EXPECT_EQ(NULL, core.GetHeader()); | ||||
|   EXPECT_EQ(0U, core.GetProgramHeaderCount()); | ||||
|   EXPECT_EQ(NULL, core.GetProgramHeader(0)); | ||||
|   EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); | ||||
|   EXPECT_FALSE(core.GetFirstNote().IsValid()); | ||||
| } | ||||
|  | ||||
| TEST(ElfCoreDumpTest, TestElfHeader) { | ||||
|   ElfCoreDump::Ehdr header; | ||||
|   memset(&header, 0, sizeof(header)); | ||||
|  | ||||
|   AutoTempDir temp_dir; | ||||
|   string core_path = temp_dir.path() + "/core"; | ||||
|   const char* core_file = core_path.c_str(); | ||||
|   MemoryMappedFile mapped_core_file; | ||||
|   ElfCoreDump core; | ||||
|  | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header) - 1)); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|   EXPECT_EQ(NULL, core.GetHeader()); | ||||
|   EXPECT_EQ(0U, core.GetProgramHeaderCount()); | ||||
|   EXPECT_EQ(NULL, core.GetProgramHeader(0)); | ||||
|   EXPECT_EQ(NULL, core.GetFirstProgramHeaderOfType(PT_LOAD)); | ||||
|   EXPECT_FALSE(core.GetFirstNote().IsValid()); | ||||
|  | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|  | ||||
|   header.e_ident[0] = ELFMAG0; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|  | ||||
|   header.e_ident[1] = ELFMAG1; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|  | ||||
|   header.e_ident[2] = ELFMAG2; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|  | ||||
|   header.e_ident[3] = ELFMAG3; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|  | ||||
|   header.e_ident[4] = ElfCoreDump::kClass; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|  | ||||
|   header.e_version = EV_CURRENT; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_FALSE(core.IsValid()); | ||||
|  | ||||
|   header.e_type = ET_CORE; | ||||
|   ASSERT_TRUE(WriteFile(core_file, &header, sizeof(header))); | ||||
|   ASSERT_TRUE(mapped_core_file.Map(core_file)); | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_TRUE(core.IsValid()); | ||||
| } | ||||
|  | ||||
| TEST(ElfCoreDumpTest, ValidCoreFile) { | ||||
|   CrashGenerator crash_generator; | ||||
|   if (!crash_generator.HasDefaultCorePattern()) { | ||||
|     fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " | ||||
|             "due to non-default core pattern"); | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   const unsigned kNumOfThreads = 3; | ||||
|   const unsigned kCrashThread = 1; | ||||
|   const int kCrashSignal = SIGABRT; | ||||
|   // TODO(benchan): Revert to use ASSERT_TRUE once the flakiness in | ||||
|   // CrashGenerator is identified and fixed. | ||||
|   if (!crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, | ||||
|                                         kCrashSignal, NULL)) { | ||||
|     fprintf(stderr, "ElfCoreDumpTest.ValidCoreFile test is skipped " | ||||
|             "due to no core dump generated"); | ||||
|     return; | ||||
|   } | ||||
|   pid_t expected_crash_thread_id = crash_generator.GetThreadId(kCrashThread); | ||||
|   set<pid_t> expected_thread_ids; | ||||
|   for (unsigned i = 0; i < kNumOfThreads; ++i) { | ||||
|     expected_thread_ids.insert(crash_generator.GetThreadId(i)); | ||||
|   } | ||||
|  | ||||
|   MemoryMappedFile mapped_core_file; | ||||
|   ASSERT_TRUE(mapped_core_file.Map(crash_generator.GetCoreFilePath().c_str())); | ||||
|  | ||||
|   ElfCoreDump core; | ||||
|   core.SetContent(mapped_core_file.content()); | ||||
|   EXPECT_TRUE(core.IsValid()); | ||||
|  | ||||
|   // Based on write_note_info() in linux/kernel/fs/binfmt_elf.c, notes are | ||||
|   // ordered as follows (NT_PRXFPREG and NT_386_TLS are i386 specific): | ||||
|   //   Thread           Name          Type | ||||
|   //   ------------------------------------------------------------------- | ||||
|   //   1st thread       CORE          NT_PRSTATUS | ||||
|   //   process-wide     CORE          NT_PRPSINFO | ||||
|   //   process-wide     CORE          NT_AUXV | ||||
|   //   1st thread       CORE          NT_FPREGSET | ||||
|   //   1st thread       LINUX         NT_PRXFPREG | ||||
|   //   1st thread       LINUX         NT_386_TLS | ||||
|   // | ||||
|   //   2nd thread       CORE          NT_PRSTATUS | ||||
|   //   2nd thread       CORE          NT_FPREGSET | ||||
|   //   2nd thread       LINUX         NT_PRXFPREG | ||||
|   //   2nd thread       LINUX         NT_386_TLS | ||||
|   // | ||||
|   //   3rd thread       CORE          NT_PRSTATUS | ||||
|   //   3rd thread       CORE          NT_FPREGSET | ||||
|   //   3rd thread       LINUX         NT_PRXFPREG | ||||
|   //   3rd thread       LINUX         NT_386_TLS | ||||
|  | ||||
|   size_t num_nt_prpsinfo = 0; | ||||
|   size_t num_nt_prstatus = 0; | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
|   size_t num_nt_fpregset = 0; | ||||
| #endif | ||||
| #if defined(__i386__) | ||||
|   size_t num_nt_prxfpreg = 0; | ||||
| #endif | ||||
|   set<pid_t> actual_thread_ids; | ||||
|   ElfCoreDump::Note note = core.GetFirstNote(); | ||||
|   while (note.IsValid()) { | ||||
|     MemoryRange name = note.GetName(); | ||||
|     MemoryRange description = note.GetDescription(); | ||||
|     EXPECT_FALSE(name.IsEmpty()); | ||||
|     EXPECT_FALSE(description.IsEmpty()); | ||||
|  | ||||
|     switch (note.GetType()) { | ||||
|       case NT_PRPSINFO: { | ||||
|         EXPECT_TRUE(description.data() != NULL); | ||||
|         EXPECT_EQ(sizeof(elf_prpsinfo), description.length()); | ||||
|         ++num_nt_prpsinfo; | ||||
|         break; | ||||
|       } | ||||
|       case NT_PRSTATUS: { | ||||
|         EXPECT_TRUE(description.data() != NULL); | ||||
|         EXPECT_EQ(sizeof(elf_prstatus), description.length()); | ||||
|         const elf_prstatus* status = description.GetData<elf_prstatus>(0); | ||||
|         actual_thread_ids.insert(status->pr_pid); | ||||
|         if (num_nt_prstatus == 0) { | ||||
|           EXPECT_EQ(expected_crash_thread_id, status->pr_pid); | ||||
|           EXPECT_EQ(kCrashSignal, status->pr_info.si_signo); | ||||
|         } | ||||
|         ++num_nt_prstatus; | ||||
|         break; | ||||
|       } | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
|       case NT_FPREGSET: { | ||||
|         EXPECT_TRUE(description.data() != NULL); | ||||
|         EXPECT_EQ(sizeof(user_fpregs_struct), description.length()); | ||||
|         ++num_nt_fpregset; | ||||
|         break; | ||||
|       } | ||||
| #endif | ||||
| #if defined(__i386__) | ||||
|       case NT_PRXFPREG: { | ||||
|         EXPECT_TRUE(description.data() != NULL); | ||||
|         EXPECT_EQ(sizeof(user_fpxregs_struct), description.length()); | ||||
|         ++num_nt_prxfpreg; | ||||
|         break; | ||||
|       } | ||||
| #endif | ||||
|       default: | ||||
|         break; | ||||
|     } | ||||
|     note = note.GetNextNote(); | ||||
|   } | ||||
|  | ||||
|   EXPECT_TRUE(expected_thread_ids == actual_thread_ids); | ||||
|   EXPECT_EQ(1U, num_nt_prpsinfo); | ||||
|   EXPECT_EQ(kNumOfThreads, num_nt_prstatus); | ||||
| #if defined(__i386__) || defined(__x86_64__) | ||||
|   EXPECT_EQ(kNumOfThreads, num_nt_fpregset); | ||||
| #endif | ||||
| #if defined(__i386__) | ||||
|   EXPECT_EQ(kNumOfThreads, num_nt_prxfpreg); | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										46
									
								
								sdk/breakpad/common/linux/elf_gnu_compat.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								sdk/breakpad/common/linux/elf_gnu_compat.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| // -*- mode: C++ -*- | ||||
|  | ||||
| // Copyright (c) 2013, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Lei Zhang <thestig@google.com> | ||||
|  | ||||
| // elf_gnu_compat.h: #defines unique to glibc's elf.h. | ||||
|  | ||||
| #ifndef COMMON_LINUX_ELF_GNU_COMPAT_H_ | ||||
| #define COMMON_LINUX_ELF_GNU_COMPAT_H_ | ||||
|  | ||||
| #include <elf.h> | ||||
|  | ||||
| // A note type on GNU systems corresponding to the .note.gnu.build-id section. | ||||
| #ifndef NT_GNU_BUILD_ID | ||||
| #define NT_GNU_BUILD_ID 3 | ||||
| #endif | ||||
|  | ||||
| #endif  // COMMON_LINUX_ELF_GNU_COMPAT_H_ | ||||
							
								
								
									
										168
									
								
								sdk/breakpad/common/linux/elf_symbols_to_module.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								sdk/breakpad/common/linux/elf_symbols_to_module.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2011 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Ted Mielczarek <ted.mielczarek@gmail.com> | ||||
|  | ||||
| #include "common/linux/elf_symbols_to_module.h" | ||||
|  | ||||
| #include <elf.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "common/byte_cursor.h" | ||||
| #include "common/module.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class ELFSymbolIterator { | ||||
| public: | ||||
|   // The contents of an ELF symbol, adjusted for the host's endianness, | ||||
|   // word size, and so on. Corresponds to the data in Elf32_Sym / Elf64_Sym. | ||||
|   struct Symbol { | ||||
|     // True if this iterator has reached the end of the symbol array. When | ||||
|     // this is set, the other members of this structure are not valid. | ||||
|     bool at_end; | ||||
|  | ||||
|     // The number of this symbol within the list. | ||||
|     size_t index; | ||||
|  | ||||
|     // The current symbol's name offset. This is the offset within the | ||||
|     // string table. | ||||
|     size_t name_offset; | ||||
|  | ||||
|     // The current symbol's value, size, info and shndx fields. | ||||
|     uint64_t value; | ||||
|     uint64_t size; | ||||
|     unsigned char info; | ||||
|     uint16_t shndx; | ||||
|   }; | ||||
|  | ||||
|   // Create an ELFSymbolIterator walking the symbols in BUFFER. Treat the | ||||
|   // symbols as big-endian if BIG_ENDIAN is true, as little-endian | ||||
|   // otherwise. Assume each symbol has a 'value' field whose size is | ||||
|   // VALUE_SIZE. | ||||
|   // | ||||
|   ELFSymbolIterator(const ByteBuffer *buffer, bool big_endian, | ||||
|                     size_t value_size) | ||||
|     : value_size_(value_size), cursor_(buffer, big_endian) { | ||||
|     // Actually, weird sizes could be handled just fine, but they're | ||||
|     // probably mistakes --- expressed in bits, say. | ||||
|     assert(value_size == 4 || value_size == 8); | ||||
|     symbol_.index = 0; | ||||
|     Fetch(); | ||||
|   } | ||||
|  | ||||
|   // Move to the next symbol. This function's behavior is undefined if | ||||
|   // at_end() is true when it is called. | ||||
|   ELFSymbolIterator &operator++() { Fetch(); symbol_.index++; return *this; } | ||||
|  | ||||
|   // Dereferencing this iterator produces a reference to an Symbol structure | ||||
|   // that holds the current symbol's values. The symbol is owned by this | ||||
|   // SymbolIterator, and will be invalidated at the next call to operator++. | ||||
|   const Symbol &operator*() const { return symbol_; } | ||||
|   const Symbol *operator->() const { return &symbol_; } | ||||
|  | ||||
| private: | ||||
|   // Read the symbol at cursor_, and set symbol_ appropriately. | ||||
|   void Fetch() { | ||||
|     // Elf32_Sym and Elf64_Sym have different layouts. | ||||
|     unsigned char other; | ||||
|     if (value_size_ == 4) { | ||||
|       // Elf32_Sym | ||||
|       cursor_ | ||||
|         .Read(4, false, &symbol_.name_offset) | ||||
|         .Read(4, false, &symbol_.value) | ||||
|         .Read(4, false, &symbol_.size) | ||||
|         .Read(1, false, &symbol_.info) | ||||
|         .Read(1, false, &other) | ||||
|         .Read(2, false, &symbol_.shndx); | ||||
|     } else { | ||||
|       // Elf64_Sym | ||||
|       cursor_ | ||||
|         .Read(4, false, &symbol_.name_offset) | ||||
|         .Read(1, false, &symbol_.info) | ||||
|         .Read(1, false, &other) | ||||
|         .Read(2, false, &symbol_.shndx) | ||||
|         .Read(8, false, &symbol_.value) | ||||
|         .Read(8, false, &symbol_.size); | ||||
|     } | ||||
|     symbol_.at_end = !cursor_; | ||||
|   } | ||||
|  | ||||
|   // The size of symbols' value field, in bytes. | ||||
|   size_t value_size_; | ||||
|  | ||||
|   // A byte cursor traversing buffer_. | ||||
|   ByteCursor cursor_; | ||||
|  | ||||
|   // Values for the symbol this iterator refers to. | ||||
|   Symbol symbol_; | ||||
| }; | ||||
|  | ||||
| const char *SymbolString(ptrdiff_t offset, ByteBuffer& strings) { | ||||
|   if (offset < 0 || (size_t) offset >= strings.Size()) { | ||||
|     // Return the null string. | ||||
|     offset = 0; | ||||
|   } | ||||
|   return reinterpret_cast<const char *>(strings.start + offset); | ||||
| } | ||||
|  | ||||
| bool ELFSymbolsToModule(const uint8_t *symtab_section, | ||||
|                         size_t symtab_size, | ||||
|                         const uint8_t *string_section, | ||||
|                         size_t string_size, | ||||
|                         const bool big_endian, | ||||
|                         size_t value_size, | ||||
|                         Module *module) { | ||||
|   ByteBuffer symbols(symtab_section, symtab_size); | ||||
|   // Ensure that the string section is null-terminated. | ||||
|   if (string_section[string_size - 1] != '\0') { | ||||
|     const void* null_terminator = memrchr(string_section, '\0', string_size); | ||||
|     string_size = reinterpret_cast<const uint8_t*>(null_terminator) | ||||
|       - string_section; | ||||
|   } | ||||
|   ByteBuffer strings(string_section, string_size); | ||||
|  | ||||
|   // The iterator walking the symbol table. | ||||
|   ELFSymbolIterator iterator(&symbols, big_endian, value_size); | ||||
|  | ||||
|   while(!iterator->at_end) { | ||||
|     if (ELF32_ST_TYPE(iterator->info) == STT_FUNC && | ||||
|         iterator->shndx != SHN_UNDEF) { | ||||
|       Module::Extern *ext = new Module::Extern; | ||||
|       ext->name = SymbolString(iterator->name_offset, strings); | ||||
|       ext->address = iterator->value; | ||||
|       module->AddExtern(ext); | ||||
|     } | ||||
|     ++iterator; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										58
									
								
								sdk/breakpad/common/linux/elf_symbols_to_module.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								sdk/breakpad/common/linux/elf_symbols_to_module.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| // -*- mode: c++ -*- | ||||
|  | ||||
| // Copyright (c) 2011 Google Inc. All Rights Reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Ted Mielczarek <ted.mielczarek@gmail.com> | ||||
|  | ||||
| // elf_symbols_to_module.h: Exposes ELFSymbolsToModule, a function | ||||
| // for reading ELF symbol tables and inserting exported symbol names | ||||
| // into a google_breakpad::Module as Extern definitions. | ||||
|  | ||||
| #ifndef BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ | ||||
| #define BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ | ||||
|  | ||||
| #include <stddef.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class Module; | ||||
|  | ||||
| bool ELFSymbolsToModule(const uint8_t *symtab_section, | ||||
|                         size_t symtab_size, | ||||
|                         const uint8_t *string_section, | ||||
|                         size_t string_size, | ||||
|                         const bool big_endian, | ||||
|                         size_t value_size, | ||||
|                         Module *module); | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
|  | ||||
| #endif  // BREAKPAD_COMMON_LINUX_ELF_SYMBOLS_TO_MODULE_H_ | ||||
							
								
								
									
										370
									
								
								sdk/breakpad/common/linux/elf_symbols_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										370
									
								
								sdk/breakpad/common/linux/elf_symbols_to_module_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,370 @@ | ||||
| // Copyright (c) 2011 Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Original author: Ted Mielczarek <ted.mielczarek@gmail.com> | ||||
|  | ||||
| // elf_symbols_to_module_unittest.cc: | ||||
| // Unittests for google_breakpad::ELFSymbolsToModule | ||||
|  | ||||
| #include <elf.h> | ||||
|  | ||||
| #include <string> | ||||
| #include <vector> | ||||
|  | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/linux/elf_symbols_to_module.h" | ||||
| #include "common/linux/synth_elf.h" | ||||
| #include "common/module.h" | ||||
| #include "common/test_assembler.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| using google_breakpad::Module; | ||||
| using google_breakpad::synth_elf::StringTable; | ||||
| using google_breakpad::test_assembler::Endianness; | ||||
| using google_breakpad::test_assembler::kBigEndian; | ||||
| using google_breakpad::test_assembler::kLittleEndian; | ||||
| using google_breakpad::test_assembler::Label; | ||||
| using google_breakpad::test_assembler::Section; | ||||
| using ::testing::Test; | ||||
| using ::testing::TestWithParam; | ||||
| using std::vector; | ||||
|  | ||||
| class ELFSymbolsToModuleTestFixture { | ||||
| public: | ||||
|   ELFSymbolsToModuleTestFixture(Endianness endianness, | ||||
|                                 size_t value_size) : module("a", "b", "c", "d"), | ||||
|                                                      section(endianness), | ||||
|                                                      table(endianness), | ||||
|                                                      value_size(value_size) {} | ||||
|  | ||||
|   bool ProcessSection() { | ||||
|     string section_contents, table_contents; | ||||
|     section.GetContents(§ion_contents); | ||||
|     table.GetContents(&table_contents); | ||||
|  | ||||
|     bool ret = ELFSymbolsToModule(reinterpret_cast<const uint8_t*>(section_contents.data()), | ||||
|                                   section_contents.size(), | ||||
|                                   reinterpret_cast<const uint8_t*>(table_contents.data()), | ||||
|                                   table_contents.size(), | ||||
|                                   section.endianness() == kBigEndian, | ||||
|                                   value_size, | ||||
|                                   &module); | ||||
|     module.GetExterns(&externs, externs.end()); | ||||
|     return ret; | ||||
|   } | ||||
|  | ||||
|   Module module; | ||||
|   Section section; | ||||
|   StringTable table; | ||||
|   string section_contents; | ||||
|   // 4 or 8 (bytes) | ||||
|   size_t value_size; | ||||
|  | ||||
|   vector<Module::Extern *> externs; | ||||
| }; | ||||
|  | ||||
| class ELFSymbolsToModuleTest32 : public ELFSymbolsToModuleTestFixture, | ||||
|                                    public TestWithParam<Endianness>  { | ||||
| public: | ||||
|   ELFSymbolsToModuleTest32() : ELFSymbolsToModuleTestFixture(GetParam(), 4) {} | ||||
|  | ||||
|   void AddElf32Sym(const string& name, uint32_t value, | ||||
|                    uint32_t size, unsigned info, uint16_t shndx) { | ||||
|     section | ||||
|       .D32(table.Add(name)) | ||||
|       .D32(value) | ||||
|       .D32(size) | ||||
|       .D8(info) | ||||
|       .D8(0) // other | ||||
|       .D16(shndx); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest32, NoFuncs) { | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)0, externs.size()); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest32, OneFunc) { | ||||
|   const string kFuncName = "superfunc"; | ||||
|   const uint32_t kFuncAddr = 0x1000; | ||||
|   const uint32_t kFuncSize = 0x10; | ||||
|  | ||||
|   AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, | ||||
|               ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 1); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)1, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest32, NameOutOfBounds) { | ||||
|   const string kFuncName = ""; | ||||
|   const uint32_t kFuncAddr = 0x1000; | ||||
|   const uint32_t kFuncSize = 0x10; | ||||
|  | ||||
|   table.Add("Foo"); | ||||
|   table.Add("Bar"); | ||||
|   // Can't use AddElf32Sym because it puts in a valid string offset. | ||||
|   section | ||||
|     .D32((uint32_t)table.Here().Value() + 1) | ||||
|     .D32(kFuncAddr) | ||||
|     .D32(kFuncSize) | ||||
|     .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) | ||||
|     .D8(0) // other | ||||
|     .D16(SHN_UNDEF + 1); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)1, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest32, NonTerminatedStringTable) { | ||||
|   const string kFuncName = ""; | ||||
|   const uint32_t kFuncAddr = 0x1000; | ||||
|   const uint32_t kFuncSize = 0x10; | ||||
|  | ||||
|   table.Add("Foo"); | ||||
|   table.Add("Bar"); | ||||
|   // Add a non-null-terminated string to the end of the string table | ||||
|   Label l; | ||||
|   table | ||||
|     .Mark(&l) | ||||
|     .Append("Unterminated"); | ||||
|   // Can't use AddElf32Sym because it puts in a valid string offset. | ||||
|   section | ||||
|     .D32((uint32_t)l.Value()) | ||||
|     .D32(kFuncAddr) | ||||
|     .D32(kFuncSize) | ||||
|     .D8(ELF32_ST_INFO(STB_GLOBAL, STT_FUNC)) | ||||
|     .D8(0) // other | ||||
|     .D16(SHN_UNDEF + 1); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)1, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest32, MultipleFuncs) { | ||||
|   const string kFuncName1 = "superfunc"; | ||||
|   const uint32_t kFuncAddr1 = 0x10001000; | ||||
|   const uint32_t kFuncSize1 = 0x10; | ||||
|   const string kFuncName2 = "awesomefunc"; | ||||
|   const uint32_t kFuncAddr2 = 0x20002000; | ||||
|   const uint32_t kFuncSize2 = 0x2f; | ||||
|   const string kFuncName3 = "megafunc"; | ||||
|   const uint32_t kFuncAddr3 = 0x30003000; | ||||
|   const uint32_t kFuncSize3 = 0x3c; | ||||
|  | ||||
|   AddElf32Sym(kFuncName1, kFuncAddr1, kFuncSize1, | ||||
|               ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 1); | ||||
|   AddElf32Sym(kFuncName2, kFuncAddr2, kFuncSize2, | ||||
|               ELF32_ST_INFO(STB_LOCAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 2); | ||||
|   AddElf32Sym(kFuncName3, kFuncAddr3, kFuncSize3, | ||||
|               ELF32_ST_INFO(STB_LOCAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 3); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)3, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName1, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); | ||||
|   Module::Extern *extern2 = externs[1]; | ||||
|   EXPECT_EQ(kFuncName2, extern2->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); | ||||
|   Module::Extern *extern3 = externs[2]; | ||||
|   EXPECT_EQ(kFuncName3, extern3->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest32, SkipStuff) { | ||||
|   const string kFuncName = "superfunc"; | ||||
|   const uint32_t kFuncAddr = 0x1000; | ||||
|   const uint32_t kFuncSize = 0x10; | ||||
|  | ||||
|   // Should skip functions in SHN_UNDEF | ||||
|   AddElf32Sym("skipme", 0xFFFF, 0x10, | ||||
|               ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               SHN_UNDEF); | ||||
|   AddElf32Sym(kFuncName, kFuncAddr, kFuncSize, | ||||
|               ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 1); | ||||
|   // Should skip non-STT_FUNC entries. | ||||
|   AddElf32Sym("skipmetoo", 0xAAAA, 0x10, | ||||
|               ELF32_ST_INFO(STB_GLOBAL, STT_FILE), | ||||
|               SHN_UNDEF + 1); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)1, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); | ||||
| } | ||||
|  | ||||
| // Run all the 32-bit tests with both endianness | ||||
| INSTANTIATE_TEST_CASE_P(Endian, | ||||
|                         ELFSymbolsToModuleTest32, | ||||
|                         ::testing::Values(kLittleEndian, kBigEndian)); | ||||
|  | ||||
| // Similar tests, but with 64-bit values. Ostensibly this could be | ||||
| // shoehorned into the parameterization by using ::testing::Combine, | ||||
| // but that would make it difficult to get the types right since these | ||||
| // actual test cases aren't parameterized. This could also be written | ||||
| // as a type-parameterized test, but combining that with a value-parameterized | ||||
| // test seemed really ugly, and also makes it harder to test 64-bit | ||||
| // values. | ||||
| class ELFSymbolsToModuleTest64 : public ELFSymbolsToModuleTestFixture, | ||||
|                                  public TestWithParam<Endianness>  { | ||||
| public: | ||||
|   ELFSymbolsToModuleTest64() : ELFSymbolsToModuleTestFixture(GetParam(), 8) {} | ||||
|  | ||||
|   void AddElf64Sym(const string& name, uint64_t value, | ||||
|                    uint64_t size, unsigned info, uint16_t shndx) { | ||||
|     section | ||||
|       .D32(table.Add(name)) | ||||
|       .D8(info) | ||||
|       .D8(0) // other | ||||
|       .D16(shndx) | ||||
|       .D64(value) | ||||
|       .D64(size); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest64, NoFuncs) { | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)0, externs.size()); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest64, OneFunc) { | ||||
|   const string kFuncName = "superfunc"; | ||||
|   const uint64_t kFuncAddr = 0x1000200030004000ULL; | ||||
|   const uint64_t kFuncSize = 0x1000; | ||||
|  | ||||
|   AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, | ||||
|               ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 1); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)1, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest64, MultipleFuncs) { | ||||
|   const string kFuncName1 = "superfunc"; | ||||
|   const uint64_t kFuncAddr1 = 0x1000100010001000ULL; | ||||
|   const uint64_t kFuncSize1 = 0x1000; | ||||
|   const string kFuncName2 = "awesomefunc"; | ||||
|   const uint64_t kFuncAddr2 = 0x2000200020002000ULL; | ||||
|   const uint64_t kFuncSize2 = 0x2f00; | ||||
|   const string kFuncName3 = "megafunc"; | ||||
|   const uint64_t kFuncAddr3 = 0x3000300030003000ULL; | ||||
|   const uint64_t kFuncSize3 = 0x3c00; | ||||
|  | ||||
|   AddElf64Sym(kFuncName1, kFuncAddr1, kFuncSize1, | ||||
|               ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 1); | ||||
|   AddElf64Sym(kFuncName2, kFuncAddr2, kFuncSize2, | ||||
|               ELF64_ST_INFO(STB_LOCAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 2); | ||||
|   AddElf64Sym(kFuncName3, kFuncAddr3, kFuncSize3, | ||||
|               ELF64_ST_INFO(STB_LOCAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 3); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)3, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName1, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr1, extern1->address); | ||||
|   Module::Extern *extern2 = externs[1]; | ||||
|   EXPECT_EQ(kFuncName2, extern2->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr2, extern2->address); | ||||
|   Module::Extern *extern3 = externs[2]; | ||||
|   EXPECT_EQ(kFuncName3, extern3->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr3, extern3->address); | ||||
| } | ||||
|  | ||||
| TEST_P(ELFSymbolsToModuleTest64, SkipStuff) { | ||||
|   const string kFuncName = "superfunc"; | ||||
|   const uint64_t kFuncAddr = 0x1000100010001000ULL; | ||||
|   const uint64_t kFuncSize = 0x1000; | ||||
|  | ||||
|   // Should skip functions in SHN_UNDEF | ||||
|   AddElf64Sym("skipme", 0xFFFF, 0x10, | ||||
|               ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               SHN_UNDEF); | ||||
|   AddElf64Sym(kFuncName, kFuncAddr, kFuncSize, | ||||
|               ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), | ||||
|               // Doesn't really matter, just can't be SHN_UNDEF. | ||||
|               SHN_UNDEF + 1); | ||||
|   // Should skip non-STT_FUNC entries. | ||||
|   AddElf64Sym("skipmetoo", 0xAAAA, 0x10, | ||||
|               ELF64_ST_INFO(STB_GLOBAL, STT_FILE), | ||||
|               SHN_UNDEF + 1); | ||||
|  | ||||
|   ProcessSection(); | ||||
|  | ||||
|   ASSERT_EQ((size_t)1, externs.size()); | ||||
|   Module::Extern *extern1 = externs[0]; | ||||
|   EXPECT_EQ(kFuncName, extern1->name); | ||||
|   EXPECT_EQ((Module::Address)kFuncAddr, extern1->address); | ||||
| } | ||||
|  | ||||
| // Run all the 64-bit tests with both endianness | ||||
| INSTANTIATE_TEST_CASE_P(Endian, | ||||
|                         ELFSymbolsToModuleTest64, | ||||
|                         ::testing::Values(kLittleEndian, kBigEndian)); | ||||
							
								
								
									
										74
									
								
								sdk/breakpad/common/linux/elfutils-inl.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								sdk/breakpad/common/linux/elfutils-inl.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,74 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #ifndef COMMON_LINUX_ELFUTILS_INL_H__ | ||||
| #define COMMON_LINUX_ELFUTILS_INL_H__ | ||||
|  | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "elfutils.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| template<typename ElfClass, typename T> | ||||
| const T* GetOffset(const typename ElfClass::Ehdr* elf_header, | ||||
|                    typename ElfClass::Off offset) { | ||||
|   return reinterpret_cast<const T*>(reinterpret_cast<uintptr_t>(elf_header) + | ||||
|                                     offset); | ||||
| } | ||||
|  | ||||
| template<typename ElfClass> | ||||
| const typename ElfClass::Shdr* FindElfSectionByName( | ||||
|     const char* name, | ||||
|     typename ElfClass::Word section_type, | ||||
|     const typename ElfClass::Shdr* sections, | ||||
|     const char* section_names, | ||||
|     const char* names_end, | ||||
|     int nsection) { | ||||
|   assert(name != NULL); | ||||
|   assert(sections != NULL); | ||||
|   assert(nsection > 0); | ||||
|  | ||||
|   int name_len = my_strlen(name); | ||||
|   if (name_len == 0) | ||||
|     return NULL; | ||||
|  | ||||
|   for (int i = 0; i < nsection; ++i) { | ||||
|     const char* section_name = section_names + sections[i].sh_name; | ||||
|     if (sections[i].sh_type == section_type && | ||||
|         names_end - section_name >= name_len + 1 && | ||||
|         my_strcmp(name, section_name) == 0) { | ||||
|       return sections + i; | ||||
|     } | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_LINUX_ELFUTILS_INL_H__ | ||||
							
								
								
									
										194
									
								
								sdk/breakpad/common/linux/elfutils.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										194
									
								
								sdk/breakpad/common/linux/elfutils.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,194 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include "common/linux/elfutils.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/linux/elfutils-inl.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| template<typename ElfClass> | ||||
| void FindElfClassSection(const char *elf_base, | ||||
|                          const char *section_name, | ||||
|                          typename ElfClass::Word section_type, | ||||
|                          const void **section_start, | ||||
|                          int *section_size) { | ||||
|   typedef typename ElfClass::Ehdr Ehdr; | ||||
|   typedef typename ElfClass::Shdr Shdr; | ||||
|  | ||||
|   assert(elf_base); | ||||
|   assert(section_start); | ||||
|   assert(section_size); | ||||
|  | ||||
|   assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0); | ||||
|  | ||||
|   const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); | ||||
|   assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); | ||||
|  | ||||
|   const Shdr* sections = | ||||
|     GetOffset<ElfClass,Shdr>(elf_header, elf_header->e_shoff); | ||||
|   const Shdr* section_names = sections + elf_header->e_shstrndx; | ||||
|   const char* names = | ||||
|     GetOffset<ElfClass,char>(elf_header, section_names->sh_offset); | ||||
|   const char *names_end = names + section_names->sh_size; | ||||
|  | ||||
|   const Shdr* section = | ||||
|     FindElfSectionByName<ElfClass>(section_name, section_type, | ||||
|                                    sections, names, names_end, | ||||
|                                    elf_header->e_shnum); | ||||
|  | ||||
|   if (section != NULL && section->sh_size > 0) { | ||||
|     *section_start = elf_base + section->sh_offset; | ||||
|     *section_size = section->sh_size; | ||||
|   } | ||||
| } | ||||
|  | ||||
| template<typename ElfClass> | ||||
| void FindElfClassSegment(const char *elf_base, | ||||
|                          typename ElfClass::Word segment_type, | ||||
|                          const void **segment_start, | ||||
|                          int *segment_size) { | ||||
|   typedef typename ElfClass::Ehdr Ehdr; | ||||
|   typedef typename ElfClass::Phdr Phdr; | ||||
|  | ||||
|   assert(elf_base); | ||||
|   assert(segment_start); | ||||
|   assert(segment_size); | ||||
|  | ||||
|   assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0); | ||||
|  | ||||
|   const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); | ||||
|   assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); | ||||
|  | ||||
|   const Phdr* phdrs = | ||||
|     GetOffset<ElfClass,Phdr>(elf_header, elf_header->e_phoff); | ||||
|  | ||||
|   for (int i = 0; i < elf_header->e_phnum; ++i) { | ||||
|     if (phdrs[i].p_type == segment_type) { | ||||
|       *segment_start = elf_base + phdrs[i].p_offset; | ||||
|       *segment_size = phdrs[i].p_filesz; | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| bool IsValidElf(const void* elf_base) { | ||||
|   return my_strncmp(reinterpret_cast<const char*>(elf_base), | ||||
|                     ELFMAG, SELFMAG) == 0; | ||||
| } | ||||
|  | ||||
| int ElfClass(const void* elf_base) { | ||||
|   const ElfW(Ehdr)* elf_header = | ||||
|     reinterpret_cast<const ElfW(Ehdr)*>(elf_base); | ||||
|  | ||||
|   return elf_header->e_ident[EI_CLASS]; | ||||
| } | ||||
|  | ||||
| bool FindElfSection(const void *elf_mapped_base, | ||||
|                     const char *section_name, | ||||
|                     uint32_t section_type, | ||||
|                     const void **section_start, | ||||
|                     int *section_size, | ||||
|                     int *elfclass) { | ||||
|   assert(elf_mapped_base); | ||||
|   assert(section_start); | ||||
|   assert(section_size); | ||||
|  | ||||
|   *section_start = NULL; | ||||
|   *section_size = 0; | ||||
|  | ||||
|   if (!IsValidElf(elf_mapped_base)) | ||||
|     return false; | ||||
|  | ||||
|   int cls = ElfClass(elf_mapped_base); | ||||
|   if (elfclass) { | ||||
|     *elfclass = cls; | ||||
|   } | ||||
|  | ||||
|   const char* elf_base = | ||||
|     static_cast<const char*>(elf_mapped_base); | ||||
|  | ||||
|   if (cls == ELFCLASS32) { | ||||
|     FindElfClassSection<ElfClass32>(elf_base, section_name, section_type, | ||||
|                                     section_start, section_size); | ||||
|     return *section_start != NULL; | ||||
|   } else if (cls == ELFCLASS64) { | ||||
|     FindElfClassSection<ElfClass64>(elf_base, section_name, section_type, | ||||
|                                     section_start, section_size); | ||||
|     return *section_start != NULL; | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| bool FindElfSegment(const void *elf_mapped_base, | ||||
|                     uint32_t segment_type, | ||||
|                     const void **segment_start, | ||||
|                     int *segment_size, | ||||
|                     int *elfclass) { | ||||
|   assert(elf_mapped_base); | ||||
|   assert(segment_start); | ||||
|   assert(segment_size); | ||||
|  | ||||
|   *segment_start = NULL; | ||||
|   *segment_size = 0; | ||||
|  | ||||
|   if (!IsValidElf(elf_mapped_base)) | ||||
|     return false; | ||||
|  | ||||
|   int cls = ElfClass(elf_mapped_base); | ||||
|   if (elfclass) { | ||||
|     *elfclass = cls; | ||||
|   } | ||||
|  | ||||
|   const char* elf_base = | ||||
|     static_cast<const char*>(elf_mapped_base); | ||||
|  | ||||
|   if (cls == ELFCLASS32) { | ||||
|     FindElfClassSegment<ElfClass32>(elf_base, segment_type, | ||||
|                                     segment_start, segment_size); | ||||
|     return *segment_start != NULL; | ||||
|   } else if (cls == ELFCLASS64) { | ||||
|     FindElfClassSegment<ElfClass64>(elf_base, segment_type, | ||||
|                                     segment_start, segment_size); | ||||
|     return *segment_start != NULL; | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										118
									
								
								sdk/breakpad/common/linux/elfutils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								sdk/breakpad/common/linux/elfutils.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| // Copyright (c) 2012, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| // | ||||
| // elfutils.h: Utilities for dealing with ELF files. | ||||
| // | ||||
|  | ||||
| #ifndef COMMON_LINUX_ELFUTILS_H__ | ||||
| #define COMMON_LINUX_ELFUTILS_H__ | ||||
|  | ||||
| #include <elf.h> | ||||
| #include <link.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| // Traits classes so consumers can write templatized code to deal | ||||
| // with specific ELF bits. | ||||
| struct ElfClass32 { | ||||
|   typedef Elf32_Addr Addr; | ||||
|   typedef Elf32_Ehdr Ehdr; | ||||
|   typedef Elf32_Nhdr Nhdr; | ||||
|   typedef Elf32_Phdr Phdr; | ||||
|   typedef Elf32_Shdr Shdr; | ||||
|   typedef Elf32_Half Half; | ||||
|   typedef Elf32_Off Off; | ||||
|   typedef Elf32_Word Word; | ||||
|   static const int kClass = ELFCLASS32; | ||||
|   static const size_t kAddrSize = sizeof(Elf32_Addr); | ||||
| }; | ||||
|  | ||||
| struct ElfClass64 { | ||||
|   typedef Elf64_Addr Addr; | ||||
|   typedef Elf64_Ehdr Ehdr; | ||||
|   typedef Elf64_Nhdr Nhdr; | ||||
|   typedef Elf64_Phdr Phdr; | ||||
|   typedef Elf64_Shdr Shdr; | ||||
|   typedef Elf64_Half Half; | ||||
|   typedef Elf64_Off Off; | ||||
|   typedef Elf64_Word Word; | ||||
|   static const int kClass = ELFCLASS64; | ||||
|   static const size_t kAddrSize = sizeof(Elf64_Addr); | ||||
| }; | ||||
|  | ||||
| bool IsValidElf(const void* elf_header); | ||||
| int ElfClass(const void* elf_base); | ||||
|  | ||||
| // Attempt to find a section named |section_name| of type |section_type| | ||||
| // in the ELF binary data at |elf_mapped_base|. On success, returns true | ||||
| // and sets |*section_start| to point to the start of the section data, | ||||
| // and |*section_size| to the size of the section's data. If |elfclass| | ||||
| // is not NULL, set |*elfclass| to the ELF file class. | ||||
| bool FindElfSection(const void *elf_mapped_base, | ||||
|                     const char *section_name, | ||||
|                     uint32_t section_type, | ||||
|                     const void **section_start, | ||||
|                     int *section_size, | ||||
|                     int *elfclass); | ||||
|  | ||||
| // Internal helper method, exposed for convenience for callers | ||||
| // that already have more info. | ||||
| template<typename ElfClass> | ||||
| const typename ElfClass::Shdr* | ||||
| FindElfSectionByName(const char* name, | ||||
|                      typename ElfClass::Word section_type, | ||||
|                      const typename ElfClass::Shdr* sections, | ||||
|                      const char* section_names, | ||||
|                      const char* names_end, | ||||
|                      int nsection); | ||||
|  | ||||
| // Attempt to find the first segment of type |segment_type| in the ELF | ||||
| // binary data at |elf_mapped_base|. On success, returns true and sets | ||||
| // |*segment_start| to point to the start of the segment data, and | ||||
| // and |*segment_size| to the size of the segment's data. If |elfclass| | ||||
| // is not NULL, set |*elfclass| to the ELF file class. | ||||
| bool FindElfSegment(const void *elf_mapped_base, | ||||
|                     uint32_t segment_type, | ||||
|                     const void **segment_start, | ||||
|                     int *segment_size, | ||||
|                     int *elfclass); | ||||
|  | ||||
| // Convert an offset from an Elf header into a pointer to the mapped | ||||
| // address in the current process. Takes an extra template parameter | ||||
| // to specify the return type to avoid having to dynamic_cast the | ||||
| // result. | ||||
| template<typename ElfClass, typename T> | ||||
| const T* | ||||
| GetOffset(const typename ElfClass::Ehdr* elf_header, | ||||
|           typename ElfClass::Off offset); | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_LINUX_ELFUTILS_H__ | ||||
							
								
								
									
										192
									
								
								sdk/breakpad/common/linux/file_id.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								sdk/breakpad/common/linux/file_id.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| // Copyright (c) 2006, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| // | ||||
| // file_id.cc: Return a unique identifier for a file | ||||
| // | ||||
| // See file_id.h for documentation | ||||
| // | ||||
|  | ||||
| #include "common/linux/file_id.h" | ||||
|  | ||||
| #include <arpa/inet.h> | ||||
| #include <assert.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include <algorithm> | ||||
|  | ||||
| #include "common/linux/elf_gnu_compat.h" | ||||
| #include "common/linux/elfutils.h" | ||||
| #include "common/linux/linux_libc_support.h" | ||||
| #include "common/linux/memory_mapped_file.h" | ||||
| #include "third_party/lss/linux_syscall_support.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| FileID::FileID(const char* path) { | ||||
|   strncpy(path_, path, sizeof(path_)); | ||||
| } | ||||
|  | ||||
| // ELF note name and desc are 32-bits word padded. | ||||
| #define NOTE_PADDING(a) ((a + 3) & ~3) | ||||
|  | ||||
| // These functions are also used inside the crashed process, so be safe | ||||
| // and use the syscall/libc wrappers instead of direct syscalls or libc. | ||||
|  | ||||
| template<typename ElfClass> | ||||
| static bool ElfClassBuildIDNoteIdentifier(const void *section, int length, | ||||
|                                           uint8_t identifier[kMDGUIDSize]) { | ||||
|   typedef typename ElfClass::Nhdr Nhdr; | ||||
|  | ||||
|   const void* section_end = reinterpret_cast<const char*>(section) + length; | ||||
|   const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section); | ||||
|   while (reinterpret_cast<const void *>(note_header) < section_end) { | ||||
|     if (note_header->n_type == NT_GNU_BUILD_ID) | ||||
|       break; | ||||
|     note_header = reinterpret_cast<const Nhdr*>( | ||||
|                   reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) + | ||||
|                   NOTE_PADDING(note_header->n_namesz) + | ||||
|                   NOTE_PADDING(note_header->n_descsz)); | ||||
|   } | ||||
|   if (reinterpret_cast<const void *>(note_header) >= section_end || | ||||
|       note_header->n_descsz == 0) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   const char* build_id = reinterpret_cast<const char*>(note_header) + | ||||
|     sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz); | ||||
|   // Copy as many bits of the build ID as will fit | ||||
|   // into the GUID space. | ||||
|   my_memset(identifier, 0, kMDGUIDSize); | ||||
|   memcpy(identifier, build_id, | ||||
|          std::min(kMDGUIDSize, (size_t)note_header->n_descsz)); | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // Attempt to locate a .note.gnu.build-id section in an ELF binary | ||||
| // and copy as many bytes of it as will fit into |identifier|. | ||||
| static bool FindElfBuildIDNote(const void *elf_mapped_base, | ||||
|                                uint8_t identifier[kMDGUIDSize]) { | ||||
|   void* note_section; | ||||
|   int note_size, elfclass; | ||||
|   if ((!FindElfSegment(elf_mapped_base, PT_NOTE, | ||||
|                        (const void**)¬e_section, ¬e_size, &elfclass) || | ||||
|       note_size == 0)  && | ||||
|       (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, | ||||
|                        (const void**)¬e_section, ¬e_size, &elfclass) || | ||||
|       note_size == 0)) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   if (elfclass == ELFCLASS32) { | ||||
|     return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size, | ||||
|                                                      identifier); | ||||
|   } else if (elfclass == ELFCLASS64) { | ||||
|     return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, note_size, | ||||
|                                                      identifier); | ||||
|   } | ||||
|  | ||||
|   return false; | ||||
| } | ||||
|  | ||||
| // Attempt to locate the .text section of an ELF binary and generate | ||||
| // a simple hash by XORing the first page worth of bytes into |identifier|. | ||||
| static bool HashElfTextSection(const void *elf_mapped_base, | ||||
|                                uint8_t identifier[kMDGUIDSize]) { | ||||
|   void* text_section; | ||||
|   int text_size; | ||||
|   if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, | ||||
|                       (const void**)&text_section, &text_size, NULL) || | ||||
|       text_size == 0) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   my_memset(identifier, 0, kMDGUIDSize); | ||||
|   const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); | ||||
|   const uint8_t* ptr_end = ptr + std::min(text_size, 4096); | ||||
|   while (ptr < ptr_end) { | ||||
|     for (unsigned i = 0; i < kMDGUIDSize; i++) | ||||
|       identifier[i] ^= ptr[i]; | ||||
|     ptr += kMDGUIDSize; | ||||
|   } | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| // static | ||||
| bool FileID::ElfFileIdentifierFromMappedFile(const void* base, | ||||
|                                              uint8_t identifier[kMDGUIDSize]) { | ||||
|   // Look for a build id note first. | ||||
|   if (FindElfBuildIDNote(base, identifier)) | ||||
|     return true; | ||||
|  | ||||
|   // Fall back on hashing the first page of the text section. | ||||
|   return HashElfTextSection(base, identifier); | ||||
| } | ||||
|  | ||||
| bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { | ||||
|   MemoryMappedFile mapped_file(path_); | ||||
|   if (!mapped_file.data())  // Should probably check if size >= ElfW(Ehdr)? | ||||
|     return false; | ||||
|  | ||||
|   return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); | ||||
| } | ||||
|  | ||||
| // static | ||||
| void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], | ||||
|                                        char* buffer, int buffer_length) { | ||||
|   uint8_t identifier_swapped[kMDGUIDSize]; | ||||
|  | ||||
|   // Endian-ness swap to match dump processor expectation. | ||||
|   memcpy(identifier_swapped, identifier, kMDGUIDSize); | ||||
|   uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped); | ||||
|   *data1 = htonl(*data1); | ||||
|   uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4); | ||||
|   *data2 = htons(*data2); | ||||
|   uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6); | ||||
|   *data3 = htons(*data3); | ||||
|  | ||||
|   int buffer_idx = 0; | ||||
|   for (unsigned int idx = 0; | ||||
|        (buffer_idx < buffer_length) && (idx < kMDGUIDSize); | ||||
|        ++idx) { | ||||
|     int hi = (identifier_swapped[idx] >> 4) & 0x0F; | ||||
|     int lo = (identifier_swapped[idx]) & 0x0F; | ||||
|  | ||||
|     if (idx == 4 || idx == 6 || idx == 8 || idx == 10) | ||||
|       buffer[buffer_idx++] = '-'; | ||||
|  | ||||
|     buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi; | ||||
|     buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo; | ||||
|   } | ||||
|  | ||||
|   // NULL terminate | ||||
|   buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0; | ||||
| } | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
							
								
								
									
										77
									
								
								sdk/breakpad/common/linux/file_id.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								sdk/breakpad/common/linux/file_id.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| // Copyright (c) 2006, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
| // | ||||
| // file_id.h: Return a unique identifier for a file | ||||
| // | ||||
|  | ||||
| #ifndef COMMON_LINUX_FILE_ID_H__ | ||||
| #define COMMON_LINUX_FILE_ID_H__ | ||||
|  | ||||
| #include <limits.h> | ||||
|  | ||||
| #include "common/linux/guid_creator.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| static const size_t kMDGUIDSize = sizeof(MDGUID); | ||||
|  | ||||
| class FileID { | ||||
|  public: | ||||
|   explicit FileID(const char* path); | ||||
|   ~FileID() {} | ||||
|  | ||||
|   // Load the identifier for the elf file path specified in the constructor into | ||||
|   // |identifier|.  Return false if the identifier could not be created for the | ||||
|   // file. | ||||
|   // The current implementation will look for a .note.gnu.build-id | ||||
|   // section and use that as the file id, otherwise it falls back to | ||||
|   // XORing the first 4096 bytes of the .text section to generate an identifier. | ||||
|   bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]); | ||||
|  | ||||
|   // Load the identifier for the elf file mapped into memory at |base| into | ||||
|   // |identifier|.  Return false if the identifier could not be created for the | ||||
|   // file. | ||||
|   static bool ElfFileIdentifierFromMappedFile(const void* base, | ||||
|                                               uint8_t identifier[kMDGUIDSize]); | ||||
|  | ||||
|   // Convert the |identifier| data to a NULL terminated string.  The string will | ||||
|   // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE). | ||||
|   // The |buffer| should be at least 37 bytes long to receive all of the data | ||||
|   // and termination.  Shorter buffers will contain truncated data. | ||||
|   static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], | ||||
|                                         char* buffer, int buffer_length); | ||||
|  | ||||
|  private: | ||||
|   // Storage for the path specified | ||||
|   char path_[PATH_MAX]; | ||||
| }; | ||||
|  | ||||
| }  // namespace google_breakpad | ||||
|  | ||||
| #endif  // COMMON_LINUX_FILE_ID_H__ | ||||
							
								
								
									
										263
									
								
								sdk/breakpad/common/linux/file_id_unittest.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								sdk/breakpad/common/linux/file_id_unittest.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | ||||
| // Copyright (c) 2010, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Unit tests for FileID | ||||
|  | ||||
| #include <elf.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/linux/elf_gnu_compat.h" | ||||
| #include "common/linux/elfutils.h" | ||||
| #include "common/linux/file_id.h" | ||||
| #include "common/linux/safe_readlink.h" | ||||
| #include "common/linux/synth_elf.h" | ||||
| #include "common/test_assembler.h" | ||||
| #include "common/tests/auto_tempdir.h" | ||||
| #include "common/using_std_string.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
|  | ||||
| using namespace google_breakpad; | ||||
| using google_breakpad::ElfClass32; | ||||
| using google_breakpad::ElfClass64; | ||||
| using google_breakpad::SafeReadLink; | ||||
| using google_breakpad::synth_elf::ELF; | ||||
| using google_breakpad::synth_elf::Notes; | ||||
| using google_breakpad::test_assembler::kLittleEndian; | ||||
| using google_breakpad::test_assembler::Section; | ||||
| using ::testing::Types; | ||||
|  | ||||
| namespace { | ||||
|  | ||||
| // Simply calling Section::Append(size, byte) produces a uninteresting pattern | ||||
| // that tends to get hashed to 0000...0000. This populates the section with | ||||
| // data to produce better hashes. | ||||
| void PopulateSection(Section* section, int size, int prime_number) { | ||||
|   for (int i = 0; i < size; i++) | ||||
|     section->Append(1, (i % prime_number) % 256); | ||||
| } | ||||
|  | ||||
| }  // namespace | ||||
|  | ||||
| #ifndef __ANDROID__ | ||||
| // This test is disabled on Android: It will always fail, since there is no | ||||
| // 'strip' binary installed on test devices. | ||||
| TEST(FileIDStripTest, StripSelf) { | ||||
|   // Calculate the File ID of this binary using | ||||
|   // FileID::ElfFileIdentifier, then make a copy of this binary, | ||||
|   // strip it, and ensure that the result is the same. | ||||
|   char exe_name[PATH_MAX]; | ||||
|   ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); | ||||
|  | ||||
|   // copy our binary to a temp file, and strip it | ||||
|   AutoTempDir temp_dir; | ||||
|   string templ = temp_dir.path() + "/file-id-unittest"; | ||||
|   char cmdline[4096]; | ||||
|   sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ.c_str()); | ||||
|   ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; | ||||
|   sprintf(cmdline, "chmod u+w \"%s\"", templ.c_str()); | ||||
|   ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; | ||||
|   sprintf(cmdline, "strip \"%s\"", templ.c_str()); | ||||
|   ASSERT_EQ(0, system(cmdline)) << "Failed to execute: " << cmdline; | ||||
|  | ||||
|   uint8_t identifier1[sizeof(MDGUID)]; | ||||
|   uint8_t identifier2[sizeof(MDGUID)]; | ||||
|   FileID fileid1(exe_name); | ||||
|   EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1)); | ||||
|   FileID fileid2(templ.c_str()); | ||||
|   EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2)); | ||||
|   char identifier_string1[37]; | ||||
|   char identifier_string2[37]; | ||||
|   FileID::ConvertIdentifierToString(identifier1, identifier_string1, | ||||
|                                     37); | ||||
|   FileID::ConvertIdentifierToString(identifier2, identifier_string2, | ||||
|                                     37); | ||||
|   EXPECT_STREQ(identifier_string1, identifier_string2); | ||||
| } | ||||
| #endif  // !__ANDROID__ | ||||
|  | ||||
| template<typename ElfClass> | ||||
| class FileIDTest : public testing::Test { | ||||
| public: | ||||
|   void GetElfContents(ELF& elf) { | ||||
|     string contents; | ||||
|     ASSERT_TRUE(elf.GetContents(&contents)); | ||||
|     ASSERT_LT(0U, contents.size()); | ||||
|  | ||||
|     elfdata_v.clear(); | ||||
|     elfdata_v.insert(elfdata_v.begin(), contents.begin(), contents.end()); | ||||
|     elfdata = &elfdata_v[0]; | ||||
|   } | ||||
|  | ||||
|   vector<uint8_t> elfdata_v; | ||||
|   uint8_t* elfdata; | ||||
| }; | ||||
|  | ||||
| typedef Types<ElfClass32, ElfClass64> ElfClasses; | ||||
|  | ||||
| TYPED_TEST_CASE(FileIDTest, ElfClasses); | ||||
|  | ||||
| TYPED_TEST(FileIDTest, ElfClass) { | ||||
|   uint8_t identifier[sizeof(MDGUID)]; | ||||
|   const char expected_identifier_string[] = | ||||
|       "80808080-8080-0000-0000-008080808080"; | ||||
|   char identifier_string[sizeof(expected_identifier_string)]; | ||||
|   const size_t kTextSectionSize = 128; | ||||
|  | ||||
|   ELF elf(EM_386, TypeParam::kClass, kLittleEndian); | ||||
|   Section text(kLittleEndian); | ||||
|   for (size_t i = 0; i < kTextSectionSize; ++i) { | ||||
|     text.D8(i * 3); | ||||
|   } | ||||
|   elf.AddSection(".text", text, SHT_PROGBITS); | ||||
|   elf.Finish(); | ||||
|   this->GetElfContents(elf); | ||||
|  | ||||
|   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, | ||||
|                                                       identifier)); | ||||
|  | ||||
|   FileID::ConvertIdentifierToString(identifier, identifier_string, | ||||
|                                     sizeof(identifier_string)); | ||||
|   EXPECT_STREQ(expected_identifier_string, identifier_string); | ||||
| } | ||||
|  | ||||
| TYPED_TEST(FileIDTest, BuildID) { | ||||
|   const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = | ||||
|     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | ||||
|      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; | ||||
|   char expected_identifier_string[] = | ||||
|     "00000000-0000-0000-0000-000000000000"; | ||||
|   FileID::ConvertIdentifierToString(kExpectedIdentifier, | ||||
|                                     expected_identifier_string, | ||||
|                                     sizeof(expected_identifier_string)); | ||||
|  | ||||
|   uint8_t identifier[sizeof(MDGUID)]; | ||||
|   char identifier_string[sizeof(expected_identifier_string)]; | ||||
|  | ||||
|   ELF elf(EM_386, TypeParam::kClass, kLittleEndian); | ||||
|   Section text(kLittleEndian); | ||||
|   text.Append(4096, 0); | ||||
|   elf.AddSection(".text", text, SHT_PROGBITS); | ||||
|   Notes notes(kLittleEndian); | ||||
|   notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier, | ||||
|                 sizeof(kExpectedIdentifier)); | ||||
|   elf.AddSection(".note.gnu.build-id", notes, SHT_NOTE); | ||||
|   elf.Finish(); | ||||
|   this->GetElfContents(elf); | ||||
|  | ||||
|   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, | ||||
|                                                       identifier)); | ||||
|  | ||||
|   FileID::ConvertIdentifierToString(identifier, identifier_string, | ||||
|                                     sizeof(identifier_string)); | ||||
|   EXPECT_STREQ(expected_identifier_string, identifier_string); | ||||
| } | ||||
|  | ||||
| TYPED_TEST(FileIDTest, BuildIDPH) { | ||||
|   const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = | ||||
|     {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, | ||||
|      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; | ||||
|   char expected_identifier_string[] = | ||||
|     "00000000-0000-0000-0000-000000000000"; | ||||
|   FileID::ConvertIdentifierToString(kExpectedIdentifier, | ||||
|                                     expected_identifier_string, | ||||
|                                     sizeof(expected_identifier_string)); | ||||
|  | ||||
|   uint8_t identifier[sizeof(MDGUID)]; | ||||
|   char identifier_string[sizeof(expected_identifier_string)]; | ||||
|  | ||||
|   ELF elf(EM_386, TypeParam::kClass, kLittleEndian); | ||||
|   Section text(kLittleEndian); | ||||
|   text.Append(4096, 0); | ||||
|   elf.AddSection(".text", text, SHT_PROGBITS); | ||||
|   Notes notes(kLittleEndian); | ||||
|   notes.AddNote(0, "Linux", | ||||
|                 reinterpret_cast<const uint8_t *>("\0x42\0x02\0\0"), 4); | ||||
|   notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier, | ||||
|                 sizeof(kExpectedIdentifier)); | ||||
|   int note_idx = elf.AddSection(".note", notes, SHT_NOTE); | ||||
|   elf.AddSegment(note_idx, note_idx, PT_NOTE); | ||||
|   elf.Finish(); | ||||
|   this->GetElfContents(elf); | ||||
|  | ||||
|   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, | ||||
|                                                       identifier)); | ||||
|  | ||||
|   FileID::ConvertIdentifierToString(identifier, identifier_string, | ||||
|                                     sizeof(identifier_string)); | ||||
|   EXPECT_STREQ(expected_identifier_string, identifier_string); | ||||
| } | ||||
|  | ||||
| // Test to make sure two files with different text sections produce | ||||
| // different hashes when not using a build id. | ||||
| TYPED_TEST(FileIDTest, UniqueHashes) { | ||||
|   char identifier_string_1[] = | ||||
|     "00000000-0000-0000-0000-000000000000"; | ||||
|   char identifier_string_2[] = | ||||
|     "00000000-0000-0000-0000-000000000000"; | ||||
|   uint8_t identifier_1[sizeof(MDGUID)]; | ||||
|   uint8_t identifier_2[sizeof(MDGUID)]; | ||||
|  | ||||
|   { | ||||
|     ELF elf1(EM_386, TypeParam::kClass, kLittleEndian); | ||||
|     Section foo_1(kLittleEndian); | ||||
|     PopulateSection(&foo_1, 32, 5); | ||||
|     elf1.AddSection(".foo", foo_1, SHT_PROGBITS); | ||||
|     Section text_1(kLittleEndian); | ||||
|     PopulateSection(&text_1, 4096, 17); | ||||
|     elf1.AddSection(".text", text_1, SHT_PROGBITS); | ||||
|     elf1.Finish(); | ||||
|     this->GetElfContents(elf1); | ||||
|   } | ||||
|  | ||||
|   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, | ||||
|                                                       identifier_1)); | ||||
|   FileID::ConvertIdentifierToString(identifier_1, identifier_string_1, | ||||
|                                     sizeof(identifier_string_1)); | ||||
|  | ||||
|   { | ||||
|     ELF elf2(EM_386, TypeParam::kClass, kLittleEndian); | ||||
|     Section text_2(kLittleEndian); | ||||
|     Section foo_2(kLittleEndian); | ||||
|     PopulateSection(&foo_2, 32, 5); | ||||
|     elf2.AddSection(".foo", foo_2, SHT_PROGBITS); | ||||
|     PopulateSection(&text_2, 4096, 31); | ||||
|     elf2.AddSection(".text", text_2, SHT_PROGBITS); | ||||
|     elf2.Finish(); | ||||
|     this->GetElfContents(elf2); | ||||
|   } | ||||
|  | ||||
|   EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, | ||||
|                                                       identifier_2)); | ||||
|   FileID::ConvertIdentifierToString(identifier_2, identifier_string_2, | ||||
|                                     sizeof(identifier_string_2)); | ||||
|  | ||||
|   EXPECT_STRNE(identifier_string_1, identifier_string_2); | ||||
| } | ||||
							
								
								
									
										199
									
								
								sdk/breakpad/common/linux/google_crashdump_uploader.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								sdk/breakpad/common/linux/google_crashdump_uploader.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
|  | ||||
| #include "common/linux/google_crashdump_uploader.h" | ||||
| #include "common/linux/libcurl_wrapper.h" | ||||
|  | ||||
| #include <sys/types.h> | ||||
| #include <sys/stat.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| #include <iostream> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| GoogleCrashdumpUploader::GoogleCrashdumpUploader(const string& product, | ||||
|                                                  const string& version, | ||||
|                                                  const string& guid, | ||||
|                                                  const string& ptime, | ||||
|                                                  const string& ctime, | ||||
|                                                  const string& email, | ||||
|                                                  const string& comments, | ||||
|                                                  const string& minidump_pathname, | ||||
|                                                  const string& crash_server, | ||||
|                                                  const string& proxy_host, | ||||
|                                                  const string& proxy_userpassword) { | ||||
|   LibcurlWrapper* http_layer = new LibcurlWrapper(); | ||||
|   Init(product, | ||||
|        version, | ||||
|        guid, | ||||
|        ptime, | ||||
|        ctime, | ||||
|        email, | ||||
|        comments, | ||||
|        minidump_pathname, | ||||
|        crash_server, | ||||
|        proxy_host, | ||||
|        proxy_userpassword, | ||||
|        http_layer); | ||||
| } | ||||
|  | ||||
| GoogleCrashdumpUploader::GoogleCrashdumpUploader(const string& product, | ||||
|                                                  const string& version, | ||||
|                                                  const string& guid, | ||||
|                                                  const string& ptime, | ||||
|                                                  const string& ctime, | ||||
|                                                  const string& email, | ||||
|                                                  const string& comments, | ||||
|                                                  const string& minidump_pathname, | ||||
|                                                  const string& crash_server, | ||||
|                                                  const string& proxy_host, | ||||
|                                                  const string& proxy_userpassword, | ||||
|                                                  LibcurlWrapper* http_layer) { | ||||
|   Init(product, | ||||
|        version, | ||||
|        guid, | ||||
|        ptime, | ||||
|        ctime, | ||||
|        email, | ||||
|        comments, | ||||
|        minidump_pathname, | ||||
|        crash_server, | ||||
|        proxy_host, | ||||
|        proxy_userpassword, | ||||
|        http_layer); | ||||
| } | ||||
|  | ||||
| void GoogleCrashdumpUploader::Init(const string& product, | ||||
|                                    const string& version, | ||||
|                                    const string& guid, | ||||
|                                    const string& ptime, | ||||
|                                    const string& ctime, | ||||
|                                    const string& email, | ||||
|                                    const string& comments, | ||||
|                                    const string& minidump_pathname, | ||||
|                                    const string& crash_server, | ||||
|                                    const string& proxy_host, | ||||
|                                    const string& proxy_userpassword, | ||||
|                                    LibcurlWrapper* http_layer) { | ||||
|   product_ = product; | ||||
|   version_ = version; | ||||
|   guid_ = guid; | ||||
|   ptime_ = ptime; | ||||
|   ctime_ = ctime; | ||||
|   email_ = email; | ||||
|   comments_ = comments; | ||||
|   http_layer_ = http_layer; | ||||
|  | ||||
|   crash_server_ = crash_server; | ||||
|   proxy_host_ = proxy_host; | ||||
|   proxy_userpassword_ = proxy_userpassword; | ||||
|   minidump_pathname_ = minidump_pathname; | ||||
|   std::cout << "Uploader initializing"; | ||||
|   std::cout << "\tProduct: " << product_; | ||||
|   std::cout << "\tVersion: " << version_; | ||||
|   std::cout << "\tGUID: " << guid_; | ||||
|   if (!ptime_.empty()) { | ||||
|     std::cout << "\tProcess uptime: " << ptime_; | ||||
|   } | ||||
|   if (!ctime_.empty()) { | ||||
|     std::cout << "\tCumulative Process uptime: " << ctime_; | ||||
|   } | ||||
|   if (!email_.empty()) { | ||||
|     std::cout << "\tEmail: " << email_; | ||||
|   } | ||||
|   if (!comments_.empty()) { | ||||
|     std::cout << "\tComments: " << comments_; | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() { | ||||
|   string error_text; | ||||
|   if (product_.empty()) { | ||||
|     error_text.append("\nProduct name must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (version_.empty()) { | ||||
|     error_text.append("\nProduct version must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (guid_.empty()) { | ||||
|     error_text.append("\nClient ID must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (minidump_pathname_.empty()) { | ||||
|     error_text.append("\nMinidump pathname must be specified."); | ||||
|   } | ||||
|  | ||||
|   if (!error_text.empty()) { | ||||
|     std::cout << error_text; | ||||
|     return false; | ||||
|   } | ||||
|   return true; | ||||
|  | ||||
| } | ||||
|  | ||||
| bool GoogleCrashdumpUploader::Upload() { | ||||
|   bool ok = http_layer_->Init(); | ||||
|   if (!ok) { | ||||
|     std::cout << "http layer init failed"; | ||||
|     return ok; | ||||
|   } | ||||
|  | ||||
|   if (!CheckRequiredParametersArePresent()) { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   struct stat st; | ||||
|   int err = stat(minidump_pathname_.c_str(), &st); | ||||
|   if (err) { | ||||
|     std::cout << minidump_pathname_ << " could not be found"; | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   parameters_["prod"] = product_; | ||||
|   parameters_["ver"] = version_; | ||||
|   parameters_["guid"] = guid_; | ||||
|   parameters_["ptime"] = ptime_; | ||||
|   parameters_["ctime"] = ctime_; | ||||
|   parameters_["email"] = email_; | ||||
|   parameters_["comments_"] = comments_; | ||||
|   if (!http_layer_->AddFile(minidump_pathname_, | ||||
|                             "upload_file_minidump")) { | ||||
|     return false; | ||||
|   } | ||||
|   std::cout << "Sending request to " << crash_server_; | ||||
|   return http_layer_->SendRequest(crash_server_, | ||||
|                                   parameters_, | ||||
|                                   NULL); | ||||
| } | ||||
| } | ||||
							
								
								
									
										100
									
								
								sdk/breakpad/common/linux/google_crashdump_uploader.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								sdk/breakpad/common/linux/google_crashdump_uploader.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| // 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 <map> | ||||
|  | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| class LibcurlWrapper; | ||||
|  | ||||
| class GoogleCrashdumpUploader { | ||||
|  public: | ||||
|   GoogleCrashdumpUploader(const string& product, | ||||
|                           const string& version, | ||||
|                           const string& guid, | ||||
|                           const string& ptime, | ||||
|                           const string& ctime, | ||||
|                           const string& email, | ||||
|                           const string& comments, | ||||
|                           const string& minidump_pathname, | ||||
|                           const string& crash_server, | ||||
|                           const string& proxy_host, | ||||
|                           const string& proxy_userpassword); | ||||
|  | ||||
|   GoogleCrashdumpUploader(const string& product, | ||||
|                           const string& version, | ||||
|                           const string& guid, | ||||
|                           const string& ptime, | ||||
|                           const string& ctime, | ||||
|                           const string& email, | ||||
|                           const string& comments, | ||||
|                           const string& minidump_pathname, | ||||
|                           const string& crash_server, | ||||
|                           const string& proxy_host, | ||||
|                           const string& proxy_userpassword, | ||||
|                           LibcurlWrapper* http_layer); | ||||
|  | ||||
|   void Init(const string& product, | ||||
|             const string& version, | ||||
|             const string& guid, | ||||
|             const string& ptime, | ||||
|             const string& ctime, | ||||
|             const string& email, | ||||
|             const string& comments, | ||||
|             const string& minidump_pathname, | ||||
|             const string& crash_server, | ||||
|             const string& proxy_host, | ||||
|             const string& proxy_userpassword, | ||||
|             LibcurlWrapper* http_layer); | ||||
|   bool Upload(); | ||||
|  | ||||
|  private: | ||||
|   bool CheckRequiredParametersArePresent(); | ||||
|  | ||||
|   LibcurlWrapper* http_layer_; | ||||
|   string product_; | ||||
|   string version_; | ||||
|   string guid_; | ||||
|   string ptime_; | ||||
|   string ctime_; | ||||
|   string email_; | ||||
|   string comments_; | ||||
|   string minidump_pathname_; | ||||
|  | ||||
|   string crash_server_; | ||||
|   string proxy_host_; | ||||
|   string proxy_userpassword_; | ||||
|  | ||||
|   std::map<string, string> parameters_; | ||||
| }; | ||||
| } | ||||
							
								
								
									
										169
									
								
								sdk/breakpad/common/linux/google_crashdump_uploader_test.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								sdk/breakpad/common/linux/google_crashdump_uploader_test.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,169 @@ | ||||
| // Copyright (c) 2009, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| // Unit test for crash dump uploader. | ||||
|  | ||||
| #include <string> | ||||
|  | ||||
| #include "common/linux/google_crashdump_uploader.h" | ||||
| #include "common/linux/libcurl_wrapper.h" | ||||
| #include "breakpad_googletest_includes.h" | ||||
| #include "common/using_std_string.h" | ||||
|  | ||||
| namespace google_breakpad { | ||||
|  | ||||
| using ::testing::Return; | ||||
| using ::testing::_; | ||||
|  | ||||
| class MockLibcurlWrapper : public LibcurlWrapper { | ||||
|  public: | ||||
|   MOCK_METHOD0(Init, bool()); | ||||
|   MOCK_METHOD2(SetProxy, bool(const string& proxy_host, | ||||
|                               const string& proxy_userpwd)); | ||||
|   MOCK_METHOD2(AddFile, bool(const string& upload_file_path, | ||||
|                              const string& basename)); | ||||
|   MOCK_METHOD3(SendRequest, | ||||
|                bool(const string& url, | ||||
|                     const std::map<string, string>& parameters, | ||||
|                     string* server_response)); | ||||
| }; | ||||
|  | ||||
| class GoogleCrashdumpUploaderTest : public ::testing::Test { | ||||
| }; | ||||
|  | ||||
| TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) { | ||||
|   MockLibcurlWrapper m; | ||||
|   EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false)); | ||||
|   GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", | ||||
|                                                                   "1.0", | ||||
|                                                                   "AAA-BBB", | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   "test@test.com", | ||||
|                                                                   "none", | ||||
|                                                                   "/tmp/foo.dmp", | ||||
|                                                                   "http://foo.com", | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   &m); | ||||
|   ASSERT_FALSE(uploader->Upload()); | ||||
| } | ||||
|  | ||||
| TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { | ||||
|   // Create a temp file | ||||
|   char tempfn[80] = "/tmp/googletest-upload-XXXXXX"; | ||||
|   int fd = mkstemp(tempfn); | ||||
|   ASSERT_NE(fd, -1); | ||||
|   close(fd); | ||||
|  | ||||
|   MockLibcurlWrapper m; | ||||
|   EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); | ||||
|   EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true)); | ||||
|   EXPECT_CALL(m, | ||||
|               SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true)); | ||||
|   GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", | ||||
|                                                                   "1.0", | ||||
|                                                                   "AAA-BBB", | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   "test@test.com", | ||||
|                                                                   "none", | ||||
|                                                                   tempfn, | ||||
|                                                                   "http://foo.com", | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   &m); | ||||
|   ASSERT_TRUE(uploader->Upload()); | ||||
| } | ||||
|  | ||||
|  | ||||
| TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { | ||||
|   MockLibcurlWrapper m; | ||||
|   EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); | ||||
|   EXPECT_CALL(m, SendRequest(_,_,_)).Times(0); | ||||
|   GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", | ||||
|                                                                   "1.0", | ||||
|                                                                   "AAA-BBB", | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   "test@test.com", | ||||
|                                                                   "none", | ||||
|                                                                   "/tmp/foo.dmp", | ||||
|                                                                   "http://foo.com", | ||||
|                                                                   "", | ||||
|                                                                   "", | ||||
|                                                                   &m); | ||||
|   ASSERT_FALSE(uploader->Upload()); | ||||
| } | ||||
|  | ||||
| TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { | ||||
|   // Test with empty product name. | ||||
|   GoogleCrashdumpUploader uploader("", | ||||
|                                    "1.0", | ||||
|                                    "AAA-BBB", | ||||
|                                    "", | ||||
|                                    "", | ||||
|                                    "test@test.com", | ||||
|                                    "none", | ||||
|                                    "/tmp/foo.dmp", | ||||
|                                    "http://foo.com", | ||||
|                                    "", | ||||
|                                    ""); | ||||
|   ASSERT_FALSE(uploader.Upload()); | ||||
|  | ||||
|   // Test with empty product version. | ||||
|   GoogleCrashdumpUploader uploader1("product", | ||||
|                                     "", | ||||
|                                     "AAA-BBB", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     "/tmp/foo.dmp", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     ""); | ||||
|  | ||||
|   ASSERT_FALSE(uploader1.Upload()); | ||||
|  | ||||
|   // Test with empty client GUID. | ||||
|   GoogleCrashdumpUploader uploader2("product", | ||||
|                                     "1.0", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     "/tmp/foo.dmp", | ||||
|                                     "", | ||||
|                                     "", | ||||
|                                     ""); | ||||
|   ASSERT_FALSE(uploader2.Upload()); | ||||
| } | ||||
| } | ||||
							
								
								
									
										104
									
								
								sdk/breakpad/common/linux/guid_creator.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								sdk/breakpad/common/linux/guid_creator.cc
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| // Copyright (c) 2006, Google Inc. | ||||
| // All rights reserved. | ||||
| // | ||||
| // Redistribution and use in source and binary forms, with or without | ||||
| // modification, are permitted provided that the following conditions are | ||||
| // met: | ||||
| // | ||||
| //     * Redistributions of source code must retain the above copyright | ||||
| // notice, this list of conditions and the following disclaimer. | ||||
| //     * Redistributions in binary form must reproduce the above | ||||
| // copyright notice, this list of conditions and the following disclaimer | ||||
| // in the documentation and/or other materials provided with the | ||||
| // distribution. | ||||
| //     * Neither the name of Google Inc. nor the names of its | ||||
| // contributors may be used to endorse or promote products derived from | ||||
| // this software without specific prior written permission. | ||||
| // | ||||
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||||
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||||
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||||
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||||
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||||
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||||
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||||
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
|  | ||||
| #include "common/linux/guid_creator.h" | ||||
|  | ||||
| #include <assert.h> | ||||
| #include <pthread.h> | ||||
| #include <stdio.h> | ||||
| #include <stdlib.h> | ||||
| #include <time.h> | ||||
| #include <unistd.h> | ||||
|  | ||||
| // | ||||
| // GUIDGenerator | ||||
| // | ||||
| // This class is used to generate random GUID. | ||||
| // Currently use random number to generate a GUID since Linux has | ||||
| // no native GUID generator. This should be OK since we don't expect | ||||
| // crash to happen very offen. | ||||
| // | ||||
| class GUIDGenerator { | ||||
|  public: | ||||
|   static uint32_t BytesToUInt32(const uint8_t bytes[]) { | ||||
|     return ((uint32_t) bytes[0] | ||||
|             | ((uint32_t) bytes[1] << 8) | ||||
|             | ((uint32_t) bytes[2] << 16) | ||||
|             | ((uint32_t) bytes[3] << 24)); | ||||
|   } | ||||
|  | ||||
|   static void UInt32ToBytes(uint8_t bytes[], uint32_t n) { | ||||
|     bytes[0] = n & 0xff; | ||||
|     bytes[1] = (n >> 8) & 0xff; | ||||
|     bytes[2] = (n >> 16) & 0xff; | ||||
|     bytes[3] = (n >> 24) & 0xff; | ||||
|   } | ||||
|  | ||||
|   static bool CreateGUID(GUID *guid) { | ||||
|     InitOnce(); | ||||
|     guid->data1 = random(); | ||||
|     guid->data2 = (uint16_t)(random()); | ||||
|     guid->data3 = (uint16_t)(random()); | ||||
|     UInt32ToBytes(&guid->data4[0], random()); | ||||
|     UInt32ToBytes(&guid->data4[4], random()); | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|  private: | ||||
|   static void InitOnce() { | ||||
|     pthread_once(&once_control, &InitOnceImpl); | ||||
|   } | ||||
|  | ||||
|   static void InitOnceImpl() { | ||||
|     srandom(time(NULL)); | ||||
|   } | ||||
|  | ||||
|   static pthread_once_t once_control; | ||||
| }; | ||||
|  | ||||
| pthread_once_t GUIDGenerator::once_control = PTHREAD_ONCE_INIT; | ||||
|  | ||||
| bool CreateGUID(GUID *guid) { | ||||
|   return GUIDGenerator::CreateGUID(guid); | ||||
| } | ||||
|  | ||||
| // Parse guid to string. | ||||
| bool GUIDToString(const GUID *guid, char *buf, int buf_len) { | ||||
|   // Should allow more space the the max length of GUID. | ||||
|   assert(buf_len > kGUIDStringLength); | ||||
|   int num = snprintf(buf, buf_len, kGUIDFormatString, | ||||
|                      guid->data1, guid->data2, guid->data3, | ||||
|                      GUIDGenerator::BytesToUInt32(&(guid->data4[0])), | ||||
|                      GUIDGenerator::BytesToUInt32(&(guid->data4[4]))); | ||||
|   if (num != kGUIDStringLength) | ||||
|     return false; | ||||
|  | ||||
|   buf[num] = '\0'; | ||||
|   return true; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user