aboutsummaryrefslogtreecommitdiff
path: root/externals/breakpad/src/processor/disassembler_objdump.cc
diff options
context:
space:
mode:
Diffstat (limited to 'externals/breakpad/src/processor/disassembler_objdump.cc')
-rw-r--r--externals/breakpad/src/processor/disassembler_objdump.cc487
1 files changed, 0 insertions, 487 deletions
diff --git a/externals/breakpad/src/processor/disassembler_objdump.cc b/externals/breakpad/src/processor/disassembler_objdump.cc
deleted file mode 100644
index 9f9569a5e6..0000000000
--- a/externals/breakpad/src/processor/disassembler_objdump.cc
+++ /dev/null
@@ -1,487 +0,0 @@
-// Copyright (c) 2022, Google LLC
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-// * Redistributions of source code must retain the above copyright
-// notice, this list of conditions and the following disclaimer.
-// * Redistributions in binary form must reproduce the above
-// copyright notice, this list of conditions and the following disclaimer
-// in the documentation and/or other materials provided with the
-// distribution.
-// * Neither the name of Google LLC nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-// disassembler_objdump.: Disassembler that invokes objdump for disassembly.
-//
-// Author: Mark Brand
-
-#ifdef HAVE_CONFIG_H
-#include <config.h> // Must come first
-#endif
-
-#include "processor/disassembler_objdump.h"
-
-#include <unistd.h>
-#include <sys/wait.h>
-
-#include <array>
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <regex>
-#include <sstream>
-#include <vector>
-
-#include "common/linux/eintr_wrapper.h"
-#include "common/linux/scoped_pipe.h"
-#include "common/linux/scoped_tmpfile.h"
-#include "processor/logging.h"
-
-namespace google_breakpad {
-namespace {
-
-const size_t kMaxX86InstructionLength = 15;
-
-bool IsInstructionPrefix(const string& token) {
- if (token == "lock" || token == "rep" || token == "repz" ||
- token == "repnz") {
- return true;
- }
- return false;
-}
-
-bool IsOperandSize(const string& token) {
- if (token == "BYTE" || token == "WORD" || token == "DWORD" ||
- token == "QWORD" || token == "PTR") {
- return true;
- }
- return false;
-}
-
-bool GetSegmentAddressX86(const DumpContext& context, string segment_name,
- uint64_t& address) {
- if (segment_name == "ds") {
- address = context.GetContextX86()->ds;
- } else if (segment_name == "es") {
- address = context.GetContextX86()->es;
- } else if (segment_name == "fs") {
- address = context.GetContextX86()->fs;
- } else if (segment_name == "gs") {
- address = context.GetContextX86()->gs;
- } else {
- BPLOG(ERROR) << "Unsupported segment register: " << segment_name;
- return false;
- }
-
- return true;
-}
-
-bool GetSegmentAddressAMD64(const DumpContext& context, string segment_name,
- uint64_t& address) {
- if (segment_name == "ds") {
- address = 0;
- } else if (segment_name == "es") {
- address = 0;
- } else {
- BPLOG(ERROR) << "Unsupported segment register: " << segment_name;
- return false;
- }
-
- return true;
-}
-
-bool GetSegmentAddress(const DumpContext& context, string segment_name,
- uint64_t& address) {
- if (context.GetContextCPU() == MD_CONTEXT_X86) {
- return GetSegmentAddressX86(context, segment_name, address);
- } else if (context.GetContextCPU() == MD_CONTEXT_AMD64) {
- return GetSegmentAddressAMD64(context, segment_name, address);
- } else {
- BPLOG(ERROR) << "Unsupported architecture for GetSegmentAddress\n";
- return false;
- }
-}
-
-bool GetRegisterValueX86(const DumpContext& context, string register_name,
- uint64_t& value) {
- if (register_name == "eax") {
- value = context.GetContextX86()->eax;
- } else if (register_name == "ebx") {
- value = context.GetContextX86()->ebx;
- } else if (register_name == "ecx") {
- value = context.GetContextX86()->ecx;
- } else if (register_name == "edx") {
- value = context.GetContextX86()->edx;
- } else if (register_name == "edi") {
- value = context.GetContextX86()->edi;
- } else if (register_name == "esi") {
- value = context.GetContextX86()->esi;
- } else if (register_name == "ebp") {
- value = context.GetContextX86()->ebp;
- } else if (register_name == "esp") {
- value = context.GetContextX86()->esp;
- } else if (register_name == "eip") {
- value = context.GetContextX86()->eip;
- } else {
- BPLOG(ERROR) << "Unsupported register: " << register_name;
- return false;
- }
-
- return true;
-}
-
-bool GetRegisterValueAMD64(const DumpContext& context, string register_name,
- uint64_t& value) {
- if (register_name == "rax") {
- value = context.GetContextAMD64()->rax;
- } else if (register_name == "rbx") {
- value = context.GetContextAMD64()->rbx;
- } else if (register_name == "rcx") {
- value = context.GetContextAMD64()->rcx;
- } else if (register_name == "rdx") {
- value = context.GetContextAMD64()->rdx;
- } else if (register_name == "rdi") {
- value = context.GetContextAMD64()->rdi;
- } else if (register_name == "rsi") {
- value = context.GetContextAMD64()->rsi;
- } else if (register_name == "rbp") {
- value = context.GetContextAMD64()->rbp;
- } else if (register_name == "rsp") {
- value = context.GetContextAMD64()->rsp;
- } else if (register_name == "r8") {
- value = context.GetContextAMD64()->r8;
- } else if (register_name == "r9") {
- value = context.GetContextAMD64()->r9;
- } else if (register_name == "r10") {
- value = context.GetContextAMD64()->r10;
- } else if (register_name == "r11") {
- value = context.GetContextAMD64()->r11;
- } else if (register_name == "r12") {
- value = context.GetContextAMD64()->r12;
- } else if (register_name == "r13") {
- value = context.GetContextAMD64()->r13;
- } else if (register_name == "r14") {
- value = context.GetContextAMD64()->r14;
- } else if (register_name == "r15") {
- value = context.GetContextAMD64()->r15;
- } else if (register_name == "rip") {
- value = context.GetContextAMD64()->rip;
- } else {
- BPLOG(ERROR) << "Unsupported register: " << register_name;
- return false;
- }
-
- return true;
-}
-
-// Lookup the value of `register_name` in `context`, store it into `value` on
-// success.
-// Support for non-full-size registers not implemented, since we're only using
-// this to evaluate address expressions.
-bool GetRegisterValue(const DumpContext& context, string register_name,
- uint64_t& value) {
- if (context.GetContextCPU() == MD_CONTEXT_X86) {
- return GetRegisterValueX86(context, register_name, value);
- } else if (context.GetContextCPU() == MD_CONTEXT_AMD64) {
- return GetRegisterValueAMD64(context, register_name, value);
- } else {
- BPLOG(ERROR) << "Unsupported architecture for GetRegisterValue\n";
- return false;
- }
-}
-} // namespace
-
-// static
-bool DisassemblerObjdump::DisassembleInstruction(uint32_t cpu,
- const uint8_t* raw_bytes,
- unsigned int raw_bytes_len,
- string& instruction) {
- // Always initialize outputs
- instruction = "";
-
- if (!raw_bytes || raw_bytes_len == 0) {
- // There's no need to perform any operation in this case, as there's
- // clearly no instruction there.
- return false;
- }
-
- string architecture;
- if (cpu == MD_CONTEXT_X86) {
- architecture = "i386";
- } else if (cpu == MD_CONTEXT_AMD64) {
- architecture = "i386:x86-64";
- } else {
- BPLOG(ERROR) << "Unsupported architecture.";
- return false;
- }
-
- // Create a temporary file for the raw instruction bytes to pass to
- // objdump, and write the bytes to the input file.
- ScopedTmpFile raw_bytes_file;
- if (!raw_bytes_file.InitData(raw_bytes, raw_bytes_len)) {
- BPLOG(ERROR) << "Failed creating temporary file.";
- return false;
- }
-
- // Create a pipe to use to read the disassembly back from objdump.
- ScopedPipe disassembly_pipe;
- if (!disassembly_pipe.Init()) {
- BPLOG(ERROR) << "Failed creating pipe for output.";
- return false;
- }
-
- pid_t child_pid = fork();
- if (child_pid < 0) {
- BPLOG(ERROR) << "Fork failed.";
- return false;
- }
-
- if (child_pid == 0) {
- // In the child process, set up the input and output file descriptors.
- if (dup2(raw_bytes_file.GetFd(), STDIN_FILENO) < 0 ||
- disassembly_pipe.Dup2WriteFd(STDOUT_FILENO) < 0 ||
- disassembly_pipe.Dup2WriteFd(STDERR_FILENO) < 0) {
- BPLOG(ERROR) << "Failed dup'ing file descriptors.";
- exit(-1);
- }
-
- // We need to close the read end of the pipe in the child process so that
- // when the parent closes it, the pipe is disconnected.
- disassembly_pipe.CloseReadFd();
-
- // We use "/proc/self/fd/0" here to allow objdump to parse an unnamed file,
- // since objdump does not have a mode to read from stdin. This cannot be
- // used with a pipe, since objdump requires that the input is a standard
- // file.
- execlp("objdump", "objdump", "-D", "--no-show-raw-insn", "-b", "binary",
- "-M", "intel", "-m", architecture.c_str(), "/proc/self/fd/0",
- nullptr);
-
- BPLOG(ERROR) << "Failed to exec objdump.";
- exit(-1);
- } else {
- // In the parent process, parse the objdump output.
-
- // Match the instruction line, from:
- // 0: lock cmpxchg DWORD PTR [esi+0x10],eax
- // extract the string "lock cmpxchg DWORD PTR [esi+0x10],eax"
- std::regex instruction_regex(
- "^\\s+[0-9a-f]+:\\s+" // " 0:"
- "((?:\\s*\\S*)+)$"); // "lock cmpxchg..."
-
- std::string line;
- std::smatch match;
- while (disassembly_pipe.ReadLine(line)) {
- if (std::regex_match(line, match, instruction_regex)) {
- instruction = match[1].str();
- break;
- }
- }
-
- // Close the read pipe so that objdump will exit (in case we broke out of
- // the loop above before reading all of the output).
- disassembly_pipe.CloseReadFd();
-
- // Now wait for objdump to exit.
- int status = 0;
- HANDLE_EINTR(waitpid(child_pid, &status, 0));
-
- if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
- BPLOG(ERROR) << "objdump didn't run successfully.";
- return false;
- }
-
- if (instruction == "") {
- BPLOG(ERROR) << "Failed to find instruction in objdump output.";
- return false;
- }
- }
-
- return true;
-}
-
-// static
-bool DisassemblerObjdump::TokenizeInstruction(const string& instruction,
- string& operation, string& dest,
- string& src) {
- // Always initialize outputs.
- operation = "";
- dest = "";
- src = "";
-
- // Split the instruction into tokens by either whitespace or comma.
- std::regex token_regex("((?:[^\\s,]+)|,)(?:\\s)*");
- std::sregex_iterator tokens_begin(instruction.begin(), instruction.end(),
- token_regex);
-
- bool found_comma = false;
- for (auto tokens_iter = tokens_begin; tokens_iter != std::sregex_iterator();
- ++tokens_iter) {
- auto token = (*tokens_iter)[1].str();
- if (operation.size() == 0) {
- if (IsInstructionPrefix(token))
- continue;
- operation = token;
- } else if (dest.size() == 0) {
- if (IsOperandSize(token))
- continue;
- dest = token;
- } else if (!found_comma) {
- if (token == ",") {
- found_comma = true;
- } else {
- BPLOG(ERROR) << "Failed to parse operands from objdump output, expected"
- " comma but found \""
- << token << "\"";
- return false;
- }
- } else if (src.size() == 0) {
- if (IsOperandSize(token))
- continue;
- src = token;
- } else {
- if (token == ",") {
- BPLOG(ERROR) << "Failed to parse operands from objdump output, found "
- "unexpected comma after last operand.";
- return false;
- } else {
- // We just ignore other junk after the last operand unless it's a
- // comma, which would indicate we're probably still in the middle
- // of the operands and something has gone wrong
- }
- }
- }
-
- if (found_comma && src.size() == 0) {
- BPLOG(ERROR) << "Failed to parse operands from objdump output, found comma "
- "but no src operand.";
- return false;
- }
-
- return true;
-}
-
-// static
-bool DisassemblerObjdump::CalculateAddress(const DumpContext& context,
- const string& expression,
- uint64_t& address) {
- address = 0;
-
- // Extract the components of the expression.
- // fs:[esi+edi*4+0x80] -> ["fs", "esi", "edi", "4", "-", "0x80"]
- std::regex expression_regex(
- "^(?:(\\ws):)?" // "fs:"
- "\\[(\\w+)" // "[esi"
- "(?:\\+(\\w+)(?:\\*(\\d+)))?" // "+edi*4"
- "(?:([\\+-])(0x[0-9a-f]+))?" // "-0x80"
- "\\]$"); // "]"
-
- std::smatch match;
- if (!std::regex_match(expression, match, expression_regex) ||
- match.size() != 7) {
- return false;
- }
-
- string segment_name = match[1].str();
- string register_name = match[2].str();
- string index_name = match[3].str();
- string index_stride = match[4].str();
- string offset_sign = match[5].str();
- string offset = match[6].str();
-
- uint64_t segment_address = 0;
- uint64_t register_value = 0;
- uint64_t index_value = 0;
- uint64_t index_stride_value = 1;
- uint64_t offset_value = 0;
-
- if (segment_name.size() &&
- !GetSegmentAddress(context, segment_name, segment_address)) {
- return false;
- }
-
- if (!GetRegisterValue(context, register_name, register_value)) {
- return false;
- }
-
- if (index_name.size() &&
- !GetRegisterValue(context, index_name, index_value)) {
- return false;
- }
-
- if (index_stride.size()) {
- index_stride_value = strtoull(index_stride.c_str(), nullptr, 0);
- }
-
- if (offset.size()) {
- offset_value = strtoull(offset.c_str(), nullptr, 0);
- }
-
- address =
- segment_address + register_value + (index_value * index_stride_value);
- if (offset_sign == "+") {
- address += offset_value;
- } else if (offset_sign == "-") {
- address -= offset_value;
- }
-
- return true;
-}
-
-DisassemblerObjdump::DisassemblerObjdump(const uint32_t cpu,
- const MemoryRegion* memory_region,
- uint64_t address) {
- if (address < memory_region->GetBase() ||
- memory_region->GetBase() + memory_region->GetSize() <= address) {
- return;
- }
-
- uint8_t ip_bytes[kMaxX86InstructionLength] = {0};
- size_t ip_bytes_length;
- for (ip_bytes_length = 0; ip_bytes_length < kMaxX86InstructionLength;
- ++ip_bytes_length) {
- // We have to read byte-by-byte here, since we still want to try and
- // disassemble an instruction even if we don't have enough bytes.
- if (!memory_region->GetMemoryAtAddress(address + ip_bytes_length,
- &ip_bytes[ip_bytes_length])) {
- break;
- }
- }
-
- string instruction;
- if (!DisassembleInstruction(cpu, ip_bytes, kMaxX86InstructionLength,
- instruction)) {
- return;
- }
-
- if (!TokenizeInstruction(instruction, operation_, dest_, src_)) {
- return;
- }
-}
-
-bool DisassemblerObjdump::CalculateSrcAddress(const DumpContext& context,
- uint64_t& address) {
- return CalculateAddress(context, src_, address);
-}
-
-bool DisassemblerObjdump::CalculateDestAddress(const DumpContext& context,
- uint64_t& address) {
- return CalculateAddress(context, dest_, address);
-}
-
-} // namespace google_breakpad \ No newline at end of file