text: //===-- ObjectFileMachO.cpp -------------------------------------*- C++ -*-===//
  //
  // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  // See https://llvm.org/LICENSE.txt for license information.
  // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  //
  //===----------------------------------------------------------------------===//
  
  #include "llvm/ADT/StringRef.h"
  
  #include "Plugins/Process/Utility/RegisterContextDarwin_arm.h"
  #include "Plugins/Process/Utility/RegisterContextDarwin_arm64.h"
  #include "Plugins/Process/Utility/RegisterContextDarwin_i386.h"
  #include "Plugins/Process/Utility/RegisterContextDarwin_x86_64.h"
  #include "lldb/Core/Debugger.h"
  #include "lldb/Core/FileSpecList.h"
  #include "lldb/Core/Module.h"
  #include "lldb/Core/ModuleSpec.h"
  #include "lldb/Core/PluginManager.h"
  #include "lldb/Core/RangeMap.h"
  #include "lldb/Core/Section.h"
  #include "lldb/Core/StreamFile.h"
  #include "lldb/Host/Host.h"
  #include "lldb/Symbol/DWARFCallFrameInfo.h"
  #include "lldb/Symbol/ObjectFile.h"
  #include "lldb/Target/DynamicLoader.h"
  #include "lldb/Target/MemoryRegionInfo.h"
  #include "lldb/Target/Platform.h"
  #include "lldb/Target/Process.h"
  #include "lldb/Target/SectionLoadList.h"
  #include "lldb/Target/SwiftLanguageRuntime.h"
  #include "lldb/Target/Target.h"
  #include "lldb/Target/Thread.h"
  #include "lldb/Target/ThreadList.h"
  #include "lldb/Utility/ArchSpec.h"
  #include "lldb/Utility/DataBuffer.h"
  #include "lldb/Utility/FileSpec.h"
  #include "lldb/Utility/Log.h"
  #include "lldb/Utility/RegisterValue.h"
  #include "lldb/Utility/Status.h"
  #include "lldb/Utility/StreamString.h"
  #include "lldb/Utility/Timer.h"
  #include "lldb/Utility/UUID.h"
  
  #include "lldb/Host/SafeMachO.h"
  
  #include "llvm/Support/MemoryBuffer.h"
  
  #include "ObjectFileMachO.h"
  
  #if defined(__APPLE__) &&                                                      \
      (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
  // GetLLDBSharedCacheUUID() needs to call dlsym()
  #include <dlfcn.h>
  #endif
  
  #ifndef __APPLE__
  #include "Utility/UuidCompatibility.h"
  #else
  #include <uuid/uuid.h>
  #endif
  
  #include <memory>
  
  #define THUMB_ADDRESS_BIT_MASK 0xfffffffffffffffeull
  using namespace lldb;
  using namespace lldb_private;
  using namespace llvm::MachO;
  
  // Some structure definitions needed for parsing the dyld shared cache files
  // found on iOS devices.
  
  struct lldb_copy_dyld_cache_header_v1 {
    char magic[16];         // e.g. "dyld_v0    i386", "dyld_v1   armv7", etc.
    uint32_t mappingOffset; // file offset to first dyld_cache_mapping_info
    uint32_t mappingCount;  // number of dyld_cache_mapping_info entries
    uint32_t imagesOffset;
    uint32_t imagesCount;
    uint64_t dyldBaseAddress;
    uint64_t codeSignatureOffset;
    uint64_t codeSignatureSize;
    uint64_t slideInfoOffset;
    uint64_t slideInfoSize;
    uint64_t localSymbolsOffset;
    uint64_t localSymbolsSize;
    uint8_t uuid[16]; // v1 and above, also recorded in dyld_all_image_infos v13
                      // and later
  };
  
  struct lldb_copy_dyld_cache_mapping_info {
    uint64_t address;
    uint64_t size;
    uint64_t fileOffset;
    uint32_t maxProt;
    uint32_t initProt;
  };
  
  struct lldb_copy_dyld_cache_local_symbols_info {
    uint32_t nlistOffset;
    uint32_t nlistCount;
    uint32_t stringsOffset;
    uint32_t stringsSize;
    uint32_t entriesOffset;
    uint32_t entriesCount;
  };
  struct lldb_copy_dyld_cache_local_symbols_entry {
    uint32_t dylibOffset;
    uint32_t nlistStartIndex;
    uint32_t nlistCount;
  };
  
  class RegisterContextDarwin_x86_64_Mach : public RegisterContextDarwin_x86_64 {
  public:
    RegisterContextDarwin_x86_64_Mach(lldb_private::Thread &thread,
                                      const DataExtractor &data)
        : RegisterContextDarwin_x86_64(thread, 0) {
      SetRegisterDataFrom_LC_THREAD(data);
    }
  
    void InvalidateAllRegisters() override {
      // Do nothing... registers are always valid...
    }
  
    void SetRegisterDataFrom_LC_THREAD(const DataExtractor &data) {
      lldb::offset_t offset = 0;
      SetError(GPRRegSet, Read, -1);
      SetError(FPURegSet, Read, -1);
      SetError(EXCRegSet, Read, -1);
      bool done = false;
  
      while (!done) {
        int flavor = data.GetU32(&offset);
        if (flavor == 0)
          done = true;
        else {
          uint32_t i;
          uint32_t count = data.GetU32(&offset);
          switch (flavor) {
          case GPRRegSet:
            for (i = 0; i < count; ++i)
              (&gpr.rax)[i] = data.GetU64(&offset);
            SetError(GPRRegSet, Read, 0);
            done = true;
  
            break;
          case FPURegSet:
            // TODO: fill in FPU regs....
            // SetError (FPURegSet, Read, -1);
            done = true;
  
            break;
          case EXCRegSet:
            exc.trapno = data.GetU32(&offset);
            exc.err = data.GetU32(&offset);
            exc.faultvaddr = data.GetU64(&offset);
            SetError(EXCRegSet, Read, 0);
            done = true;
            break;
          case 7:
          case 8:
          case 9:
            // fancy flavors that encapsulate of the above flavors...
            break;
  
          default:
            done = true;
            break;
          }
        }
      }
    }
  
    static size_t WriteRegister(RegisterContext *reg_ctx, const char *name,
                                const char *alt_name, size_t reg_byte_size,
                                Stream &data) {
      const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
      if (reg_info == NULL)
        reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
      if (reg_info) {
        lldb_private::RegisterValue reg_value;
        if (reg_ctx->ReadRegister(reg_info, reg_value)) {
          if (reg_info->byte_size >= reg_byte_size)
            data.Write(reg_value.GetBytes(), reg_byte_size);
          else {
            data.Write(reg_value.GetBytes(), reg_info->byte_size);
            for (size_t i = 0, n = reg_byte_size - reg_info->byte_size; i < n;
                 ++i)
              data.PutChar(0);
          }
          return reg_byte_size;
        }
      }
      // Just write zeros if all else fails
      for (size_t i = 0; i < reg_byte_size; ++i)
        data.PutChar(0);
      return reg_byte_size;
    }
  
    static bool Create_LC_THREAD(Thread *thread, Stream &data) {
      RegisterContextSP reg_ctx_sp(thread->GetRegisterContext());
      if (reg_ctx_sp) {
        RegisterContext *reg_ctx = reg_ctx_sp.get();
  
        data.PutHex32(GPRRegSet); // Flavor
        data.PutHex32(GPRWordCount);
        WriteRegister(reg_ctx, "rax", NULL, 8, data);
        WriteRegister(reg_ctx, "rbx", NULL, 8, data);
        WriteRegister(reg_ctx, "rcx", NULL, 8, data);
        WriteRegister(reg_ctx, "rdx", NULL, 8, data);
        WriteRegister(reg_ctx, "rdi", NULL, 8, data);
        WriteRegister(reg_ctx, "rsi", NULL, 8, data);
        WriteRegister(reg_ctx, "rbp", NULL, 8, data);
        WriteRegister(reg_ctx, "rsp", NULL, 8, data);
        WriteRegister(reg_ctx, "r8", NULL, 8, data);
        WriteRegister(reg_ctx, "r9", NULL, 8, data);
        WriteRegister(reg_ctx, "r10", NULL, 8, data);
        WriteRegister(reg_ctx, "r11", NULL, 8, data);
        WriteRegister(reg_ctx, "r12", NULL, 8, data);
        WriteRegister(reg_ctx, "r13", NULL, 8, data);
        WriteRegister(reg_ctx, "r14", NULL, 8, data);
        WriteRegister(reg_ctx, "r15", NULL, 8, data);
        WriteRegister(reg_ctx, "rip", NULL, 8, data);
        WriteRegister(reg_ctx, "rflags", NULL, 8, data);
        WriteRegister(reg_ctx, "cs", NULL, 8, data);
        WriteRegister(reg_ctx, "fs", NULL, 8, data);
        WriteRegister(reg_ctx, "gs", NULL, 8, data);
  
        //            // Write out the FPU registers
        //            const size_t fpu_byte_size = sizeof(FPU);
        //            size_t bytes_written = 0;
        //            data.PutHex32 (FPURegSet);
        //            data.PutHex32 (fpu_byte_size/sizeof(uint64_t));
        //            bytes_written += data.PutHex32(0); // uint32_t pad[0]
        //            bytes_written += data.PutHex32(0); // uint32_t pad[1]
        //            bytes_written += WriteRegister (reg_ctx, "fcw", "fctrl", 2,
        //            data);   // uint16_t    fcw;    // "fctrl"
        //            bytes_written += WriteRegister (reg_ctx, "fsw" , "fstat", 2,
        //            data);  // uint16_t    fsw;    // "fstat"
        //            bytes_written += WriteRegister (reg_ctx, "ftw" , "ftag", 1,
        //            data);   // uint8_t     ftw;    // "ftag"
        //            bytes_written += data.PutHex8  (0); // uint8_t pad1;
        //            bytes_written += WriteRegister (reg_ctx, "fop" , NULL, 2,
        //            data);     // uint16_t    fop;    // "fop"
        //            bytes_written += WriteRegister (reg_ctx, "fioff", "ip", 4,
        //            data);    // uint32_t    ip;     // "fioff"
        //            bytes_written += WriteRegister (reg_ctx, "fiseg", NULL, 2,
        //            data);    // uint16_t    cs;     // "fiseg"
        //            bytes_written += data.PutHex16 (0); // uint16_t    pad2;
        //            bytes_written += WriteRegister (reg_ctx, "dp", "fooff" , 4,
        //            data);   // uint32_t    dp;     // "fooff"
        //            bytes_written += WriteRegister (reg_ctx, "foseg", NULL, 2,
        //            data);    // uint16_t    ds;     // "foseg"
        //            bytes_written += data.PutHex16 (0); // uint16_t    pad3;
        //            bytes_written += WriteRegister (reg_ctx, "mxcsr", NULL, 4,
        //            data);    // uint32_t    mxcsr;
        //            bytes_written += WriteRegister (reg_ctx, "mxcsrmask", NULL,
        //            4, data);// uint32_t    mxcsrmask;
        //            bytes_written += WriteRegister (reg_ctx, "stmm0", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "stmm1", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "stmm2", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "stmm3", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "stmm4", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "stmm5", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "stmm6", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "stmm7", NULL,
        //            sizeof(MMSReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm0" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm1" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm2" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm3" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm4" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm5" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm6" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm7" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm8" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm9" , NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm10", NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm11", NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm12", NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm13", NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm14", NULL,
        //            sizeof(XMMReg), data);
        //            bytes_written += WriteRegister (reg_ctx, "xmm15", NULL,
        //            sizeof(XMMReg), data);
        //
        //            // Fill rest with zeros
        //            for (size_t i=0, n = fpu_byte_size - bytes_written; i<n; ++
        //            i)
        //                data.PutChar(0);
  
        // Write out the EXC registers
        data.PutHex32(EXCRegSet);
        data.PutHex32(EXCWordCount);
        WriteRegister(reg_ctx, "trapno", NULL, 4, data);
        WriteRegister(reg_ctx, "err", NULL, 4, data);
        WriteRegister(reg_ctx, "faultvaddr", NULL, 8, data);
        return true;
      }
      return false;
    }
  
  protected:
    int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override { return 0; }
  
    int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override { return 0; }
  
    int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override { return 0; }
  
    int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override {
      return 0;
    }
  
    int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override {
      return 0;
    }
  
    int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override {
      return 0;
    }
  };
  
  class RegisterContextDarwin_i386_Mach : public RegisterContextDarwin_i386 {
  public:
    RegisterContextDarwin_i386_Mach(lldb_private::Thread &thread,
                                    const DataExtractor &data)
        : RegisterContextDarwin_i386(thread, 0) {
      SetRegisterDataFrom_LC_THREAD(data);
    }
  
    void InvalidateAllRegisters() override {
      // Do nothing... registers are always valid...
    }
  
    void SetRegisterDataFrom_LC_THREAD(const DataExtractor &data) {
      lldb::offset_t offset = 0;
      SetError(GPRRegSet, Read, -1);
      SetError(FPURegSet, Read, -1);
      SetError(EXCRegSet, Read, -1);
      bool done = false;
  
      while (!done) {
        int flavor = data.GetU32(&offset);
        if (flavor == 0)
          done = true;
        else {
          uint32_t i;
          uint32_t count = data.GetU32(&offset);
          switch (flavor) {
          case GPRRegSet:
            for (i = 0; i < count; ++i)
              (&gpr.eax)[i] = data.GetU32(&offset);
            SetError(GPRRegSet, Read, 0);
            done = true;
  
            break;
          case FPURegSet:
            // TODO: fill in FPU regs....
            // SetError (FPURegSet, Read, -1);
            done = true;
  
            break;
          case EXCRegSet:
            exc.trapno = data.GetU32(&offset);
            exc.err = data.GetU32(&offset);
            exc.faultvaddr = data.GetU32(&offset);
            SetError(EXCRegSet, Read, 0);
            done = true;
            break;
          case 7:
          case 8:
          case 9:
            // fancy flavors that encapsulate of the above flavors...
            break;
  
          default:
            done = true;
            break;
          }
        }
      }
    }
  
    static size_t WriteRegister(RegisterContext *reg_ctx, const char *name,
                                const char *alt_name, size_t reg_byte_size,
                                Stream &data) {
      const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
      if (reg_info == NULL)
        reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
      if (reg_info) {
        lldb_private::RegisterValue reg_value;
        if (reg_ctx->ReadRegister(reg_info, reg_value)) {
          if (reg_info->byte_size >= reg_byte_size)
            data.Write(reg_value.GetBytes(), reg_byte_size);
          else {
            data.Write(reg_value.GetBytes(), reg_info->byte_size);
            for (size_t i = 0, n = reg_byte_size - reg_info->byte_size; i < n;
                 ++i)
              data.PutChar(0);
          }
          return reg_byte_size;
        }
      }
      // Just write zeros if all else fails
      for (size_t i = 0; i < reg_byte_size; ++i)
        data.PutChar(0);
      return reg_byte_size;
    }
  
    static bool Create_LC_THREAD(Thread *thread, Stream &data) {
      RegisterContextSP reg_ctx_sp(thread->GetRegisterContext());
      if (reg_ctx_sp) {
        RegisterContext *reg_ctx = reg_ctx_sp.get();
  
        data.PutHex32(GPRRegSet); // Flavor
        data.PutHex32(GPRWordCount);
        WriteRegister(reg_ctx, "eax", NULL, 4, data);
        WriteRegister(reg_ctx, "ebx", NULL, 4, data);
        WriteRegister(reg_ctx, "ecx", NULL, 4, data);
        WriteRegister(reg_ctx, "edx", NULL, 4, data);
        WriteRegister(reg_ctx, "edi", NULL, 4, data);
        WriteRegister(reg_ctx, "esi", NULL, 4, data);
        WriteRegister(reg_ctx, "ebp", NULL, 4, data);
        WriteRegister(reg_ctx, "esp", NULL, 4, data);
        WriteRegister(reg_ctx, "ss", NULL, 4, data);
        WriteRegister(reg_ctx, "eflags", NULL, 4, data);
        WriteRegister(reg_ctx, "eip", NULL, 4, data);
        WriteRegister(reg_ctx, "cs", NULL, 4, data);
        WriteRegister(reg_ctx, "ds", NULL, 4, data);
        WriteRegister(reg_ctx, "es", NULL, 4, data);
        WriteRegister(reg_ctx, "fs", NULL, 4, data);
        WriteRegister(reg_ctx, "gs", NULL, 4, data);
  
        // Write out the EXC registers
        data.PutHex32(EXCRegSet);
        data.PutHex32(EXCWordCount);
        WriteRegister(reg_ctx, "trapno", NULL, 4, data);
        WriteRegister(reg_ctx, "err", NULL, 4, data);
        WriteRegister(reg_ctx, "faultvaddr", NULL, 4, data);
        return true;
      }
      return false;
    }
  
  protected:
    int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override { return 0; }
  
    int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override { return 0; }
  
    int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override { return 0; }
  
    int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override {
      return 0;
    }
  
    int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override {
      return 0;
    }
  
    int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override {
      return 0;
    }
  };
  
  class RegisterContextDarwin_arm_Mach : public RegisterContextDarwin_arm {
  public:
    RegisterContextDarwin_arm_Mach(lldb_private::Thread &thread,
                                   const DataExtractor &data)
        : RegisterContextDarwin_arm(thread, 0) {
      SetRegisterDataFrom_LC_THREAD(data);
    }
  
    void InvalidateAllRegisters() override {
      // Do nothing... registers are always valid...
    }
  
    void SetRegisterDataFrom_LC_THREAD(const DataExtractor &data) {
      lldb::offset_t offset = 0;
      SetError(GPRRegSet, Read, -1);
      SetError(FPURegSet, Read, -1);
      SetError(EXCRegSet, Read, -1);
      bool done = false;
  
      while (!done) {
        int flavor = data.GetU32(&offset);
        uint32_t count = data.GetU32(&offset);
        lldb::offset_t next_thread_state = offset + (count * 4);
        switch (flavor) {
        case GPRAltRegSet:
        case GPRRegSet:
          for (uint32_t i = 0; i < count; ++i) {
            gpr.r[i] = data.GetU32(&offset);
          }
  
          // Note that gpr.cpsr is also copied by the above loop; this loop
          // technically extends one element past the end of the gpr.r[] array.
  
          SetError(GPRRegSet, Read, 0);
          offset = next_thread_state;
          break;
  
        case FPURegSet: {
          uint8_t *fpu_reg_buf = (uint8_t *)&fpu.floats.s[0];
          const int fpu_reg_buf_size = sizeof(fpu.floats);
          if (data.ExtractBytes(offset, fpu_reg_buf_size, eByteOrderLittle,
                                fpu_reg_buf) == fpu_reg_buf_size) {
            offset += fpu_reg_buf_size;
            fpu.fpscr = data.GetU32(&offset);
            SetError(FPURegSet, Read, 0);
          } else {
            done = true;
          }
        }
          offset = next_thread_state;
          break;
  
        case EXCRegSet:
          if (count == 3) {
            exc.exception = data.GetU32(&offset);
            exc.fsr = data.GetU32(&offset);
            exc.far = data.GetU32(&offset);
            SetError(EXCRegSet, Read, 0);
          }
          done = true;
          offset = next_thread_state;
          break;
  
        // Unknown register set flavor, stop trying to parse.
        default:
          done = true;
        }
      }
    }
  
    static size_t WriteRegister(RegisterContext *reg_ctx, const char *name,
                                const char *alt_name, size_t reg_byte_size,
                                Stream &data) {
      const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
      if (reg_info == NULL)
        reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
      if (reg_info) {
        lldb_private::RegisterValue reg_value;
        if (reg_ctx->ReadRegister(reg_info, reg_value)) {
          if (reg_info->byte_size >= reg_byte_size)
            data.Write(reg_value.GetBytes(), reg_byte_size);
          else {
            data.Write(reg_value.GetBytes(), reg_info->byte_size);
            for (size_t i = 0, n = reg_byte_size - reg_info->byte_size; i < n;
                 ++i)
              data.PutChar(0);
          }
          return reg_byte_size;
        }
      }
      // Just write zeros if all else fails
      for (size_t i = 0; i < reg_byte_size; ++i)
        data.PutChar(0);
      return reg_byte_size;
    }
  
    static bool Create_LC_THREAD(Thread *thread, Stream &data) {
      RegisterContextSP reg_ctx_sp(thread->GetRegisterContext());
      if (reg_ctx_sp) {
        RegisterContext *reg_ctx = reg_ctx_sp.get();
  
        data.PutHex32(GPRRegSet); // Flavor
        data.PutHex32(GPRWordCount);
        WriteRegister(reg_ctx, "r0", NULL, 4, data);
        WriteRegister(reg_ctx, "r1", NULL, 4, data);
        WriteRegister(reg_ctx, "r2", NULL, 4, data);
        WriteRegister(reg_ctx, "r3", NULL, 4, data);
        WriteRegister(reg_ctx, "r4", NULL, 4, data);
        WriteRegister(reg_ctx, "r5", NULL, 4, data);
        WriteRegister(reg_ctx, "r6", NULL, 4, data);
        WriteRegister(reg_ctx, "r7", NULL, 4, data);
        WriteRegister(reg_ctx, "r8", NULL, 4, data);
        WriteRegister(reg_ctx, "r9", NULL, 4, data);
        WriteRegister(reg_ctx, "r10", NULL, 4, data);
        WriteRegister(reg_ctx, "r11", NULL, 4, data);
        WriteRegister(reg_ctx, "r12", NULL, 4, data);
        WriteRegister(reg_ctx, "sp", NULL, 4, data);
        WriteRegister(reg_ctx, "lr", NULL, 4, data);
        WriteRegister(reg_ctx, "pc", NULL, 4, data);
        WriteRegister(reg_ctx, "cpsr", NULL, 4, data);
  
        // Write out the EXC registers
        //            data.PutHex32 (EXCRegSet);
        //            data.PutHex32 (EXCWordCount);
        //            WriteRegister (reg_ctx, "exception", NULL, 4, data);
        //            WriteRegister (reg_ctx, "fsr", NULL, 4, data);
        //            WriteRegister (reg_ctx, "far", NULL, 4, data);
        return true;
      }
      return false;
    }
  
  protected:
    int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override { return -1; }
  
    int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override { return -1; }
  
    int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override { return -1; }
  
    int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) override { return -1; }
  
    int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override {
      return 0;
    }
  
    int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override {
      return 0;
    }
  
    int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override {
      return 0;
    }
  
    int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) override {
      return -1;
    }
  };
  
  class RegisterContextDarwin_arm64_Mach : public RegisterContextDarwin_arm64 {
  public:
    RegisterContextDarwin_arm64_Mach(lldb_private::Thread &thread,
                                     const DataExtractor &data)
        : RegisterContextDarwin_arm64(thread, 0) {
      SetRegisterDataFrom_LC_THREAD(data);
    }
  
    void InvalidateAllRegisters() override {
      // Do nothing... registers are always valid...
    }
  
    void SetRegisterDataFrom_LC_THREAD(const DataExtractor &data) {
      lldb::offset_t offset = 0;
      SetError(GPRRegSet, Read, -1);
      SetError(FPURegSet, Read, -1);
      SetError(EXCRegSet, Read, -1);
      bool done = false;
      while (!done) {
        int flavor = data.GetU32(&offset);
        uint32_t count = data.GetU32(&offset);
        lldb::offset_t next_thread_state = offset + (count * 4);
        switch (flavor) {
        case GPRRegSet:
          // x0-x29 + fp + lr + sp + pc (== 33 64-bit registers) plus cpsr (1
          // 32-bit register)
          if (count >= (33 * 2) + 1) {
            for (uint32_t i = 0; i < 29; ++i)
              gpr.x[i] = data.GetU64(&offset);
            gpr.fp = data.GetU64(&offset);
            gpr.lr = data.GetU64(&offset);
            gpr.sp = data.GetU64(&offset);
            gpr.pc = data.GetU64(&offset);
            gpr.cpsr = data.GetU32(&offset);
            SetError(GPRRegSet, Read, 0);
          }
          offset = next_thread_state;
          break;
        case FPURegSet: {
          uint8_t *fpu_reg_buf = (uint8_t *)&fpu.v[0];
          const int fpu_reg_buf_size = sizeof(fpu);
          if (fpu_reg_buf_size == count * sizeof(uint32_t) &&
              data.ExtractBytes(offset, fpu_reg_buf_size, eByteOrderLittle,
                                fpu_reg_buf) == fpu_reg_buf_size) {
            SetError(FPURegSet, Read, 0);
          } else {
            done = true;
          }
        }
          offset = next_thread_state;
          break;
        case EXCRegSet:
          if (count == 4) {
            exc.far = data.GetU64(&offset);
            exc.esr = data.GetU32(&offset);
            exc.exception = data.GetU32(&offset);
            SetError(EXCRegSet, Read, 0);
          }
          offset = next_thread_state;
          break;
        default:
          done = true;
          break;
        }
      }
    }
  
    static size_t WriteRegister(RegisterContext *reg_ctx, const char *name,
                                const char *alt_name, size_t reg_byte_size,
                                Stream &data) {
      const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoByName(name);
      if (reg_info == NULL)
        reg_info = reg_ctx->GetRegisterInfoByName(alt_name);
      if (reg_info) {
        lldb_private::RegisterValue reg_value;
        if (reg_ctx->ReadRegister(reg_info, reg_value)) {
          if (reg_info->byte_size >= reg_byte_size)
            data.Write(reg_value.GetBytes(), reg_byte_size);
          else {
            data.Write(reg_value.GetBytes(), reg_info->byte_size);
            for (size_t i = 0, n = reg_byte_size - reg_info->byte_size; i < n;
                 ++i)
              data.PutChar(0);
          }
          return reg_byte_size;
        }
      }
      // Just write zeros if all else fails
      for (size_t i = 0; i < reg_byte_size; ++i)
        data.PutChar(0);
      return reg_byte_size;
    }
  
    static bool Create_LC_THREAD(Thread *thread, Stream &data) {
      RegisterContextSP reg_ctx_sp(thread->GetRegisterContext());
      if (reg_ctx_sp) {
        RegisterContext *reg_ctx = reg_ctx_sp.get();
  
        data.PutHex32(GPRRegSet); // Flavor
        data.PutHex32(GPRWordCount);
        WriteRegister(reg_ctx, "x0", NULL, 8, data);
        WriteRegister(reg_ctx, "x1", NULL, 8, data);
        WriteRegister(reg_ctx, "x2", NULL, 8, data);
        WriteRegister(reg_ctx, "x3", NULL, 8, data);
        WriteRegister(reg_ctx, "x4", NULL, 8, data);
        WriteRegister(reg_ctx, "x5", NULL, 8, data);
        WriteRegister(reg_ctx, "x6", NULL, 8, data);
        WriteRegister(reg_ctx, "x7", NULL, 8, data);
        WriteRegister(reg_ctx, "x8", NULL, 8, data);
        WriteRegister(reg_ctx, "x9", NULL, 8, data);
        WriteRegister(reg_ctx, "x10", NULL, 8, data);
        WriteRegister(reg_ctx, "x11", NULL, 8, data);
        WriteRegister(reg_ctx, "x12", NULL, 8, data);
        WriteRegister(reg_ctx, "x13", NULL, 8, data);
        WriteRegister(reg_ctx, "x14", NULL, 8, data);
        WriteRegister(reg_ctx, "x15", NULL, 8, data);
        WriteRegister(reg_ctx, "x16", NULL, 8, data);
        WriteRegister(reg_ctx, "x17", NULL, 8, data);
        WriteRegister(reg_ctx, "x18", NULL, 8, data);
        WriteRegister(reg_ctx, "x19", NULL, 8, data);
        WriteRegister(reg_ctx, "x20", NULL, 8, data);
        WriteRegister(reg_ctx, "x21", NULL, 8, data);
        WriteRegister(reg_ctx, "x22", NULL, 8, data);
        WriteRegister(reg_ctx, "x23", NULL, 8, data);
        WriteRegister(reg_ctx, "x24", NULL, 8, data);
        WriteRegister(reg_ctx, "x25", NULL, 8, data);
        WriteRegister(reg_ctx, "x26", NULL, 8, data);
        WriteRegister(reg_ctx, "x27", NULL, 8, data);
        WriteRegister(reg_ctx, "x28", NULL, 8, data);
        WriteRegister(reg_ctx, "fp", NULL, 8, data);
        WriteRegister(reg_ctx, "lr", NULL, 8, data);
        WriteRegister(reg_ctx, "sp", NULL, 8, data);
        WriteRegister(reg_ctx, "pc", NULL, 8, data);
        WriteRegister(reg_ctx, "cpsr", NULL, 4, data);
  
        // Write out the EXC registers
        //            data.PutHex32 (EXCRegSet);
        //            data.PutHex32 (EXCWordCount);
        //            WriteRegister (reg_ctx, "far", NULL, 8, data);
        //            WriteRegister (reg_ctx, "esr", NULL, 4, data);
        //            WriteRegister (reg_ctx, "exception", NULL, 4, data);
        return true;
      }
      return false;
    }
  
  protected:
    int DoReadGPR(lldb::tid_t tid, int flavor, GPR &gpr) override { return -1; }
  
    int DoReadFPU(lldb::tid_t tid, int flavor, FPU &fpu) override { return -1; }
  
    int DoReadEXC(lldb::tid_t tid, int flavor, EXC &exc) override { return -1; }
  
    int DoReadDBG(lldb::tid_t tid, int flavor, DBG &dbg) override { return -1; }
  
    int DoWriteGPR(lldb::tid_t tid, int flavor, const GPR &gpr) override {
      return 0;
    }
  
    int DoWriteFPU(lldb::tid_t tid, int flavor, const FPU &fpu) override {
      return 0;
    }
  
    int DoWriteEXC(lldb::tid_t tid, int flavor, const EXC &exc) override {
      return 0;
    }
  
    int DoWriteDBG(lldb::tid_t tid, int flavor, const DBG &dbg) override {
      return -1;
    }
  };
  
  static uint32_t MachHeaderSizeFromMagic(uint32_t magic) {
    switch (magic) {
    case MH_MAGIC:
    case MH_CIGAM:
      return sizeof(struct mach_header);
  
    case MH_MAGIC_64:
    case MH_CIGAM_64:
      return sizeof(struct mach_header_64);
      break;
  
    default:
      break;
    }
    return 0;
  }
  
  #define MACHO_NLIST_ARM_SYMBOL_IS_THUMB 0x0008
  
  void ObjectFileMachO::Initialize() {
    PluginManager::RegisterPlugin(
        GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
        CreateMemoryInstance, GetModuleSpecifications, SaveCore);
  }
  
  void ObjectFileMachO::Terminate() {
    PluginManager::UnregisterPlugin(CreateInstance);
  }
  
  lldb_private::ConstString ObjectFileMachO::GetPluginNameStatic() {
    static ConstString g_name("mach-o");
    return g_name;
  }
  
  const char *ObjectFileMachO::GetPluginDescriptionStatic() {
    return "Mach-o object file reader (32 and 64 bit)";
  }
  
  ObjectFile *ObjectFileMachO::CreateInstance(const lldb::ModuleSP &module_sp,
                                              DataBufferSP &data_sp,
                                              lldb::offset_t data_offset,
                                              const FileSpec *file,
                                              lldb::offset_t file_offset,
                                              lldb::offset_t length) {
    if (!data_sp) {
      data_sp = MapFileData(*file, length, file_offset);
      if (!data_sp)
        return nullptr;
      data_offset = 0;
    }
  
    if (!ObjectFileMachO::MagicBytesMatch(data_sp, data_offset, length))
      return nullptr;
  
    // Update the data to contain the entire file if it doesn't already
    if (data_sp->GetByteSize() < length) {
      data_sp = MapFileData(*file, length, file_offset);
      if (!data_sp)
        return nullptr;
      data_offset = 0;
    }
    auto objfile_up = llvm::make_unique<ObjectFileMachO>(
        module_sp, data_sp, data_offset, file, file_offset, length);
    if (!objfile_up || !objfile_up->ParseHeader())
      return nullptr;
  
    return objfile_up.release();
  }
  
  ObjectFile *ObjectFileMachO::CreateMemoryInstance(
      const lldb::ModuleSP &module_sp, DataBufferSP &data_sp,
      const ProcessSP &process_sp, lldb::addr_t header_addr) {
    if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) {
      std::unique_ptr<ObjectFile> objfile_up(
          new ObjectFileMachO(module_sp, data_sp, process_sp, header_addr));
      if (objfile_up.get() && objfile_up->ParseHeader())
        return objfile_up.release();
    }
    return NULL;
  }
  
  size_t ObjectFileMachO::GetModuleSpecifications(
      const lldb_private::FileSpec &file, lldb::DataBufferSP &data_sp,
      lldb::offset_t data_offset, lldb::offset_t file_offset,
      lldb::offset_t length, lldb_private::ModuleSpecList &specs) {
    const size_t initial_count = specs.GetSize();
  
    if (ObjectFileMachO::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) {
      DataExtractor data;
      data.SetData(data_sp);
      llvm::MachO::mach_header header;
      if (ParseHeader(data, &data_offset, header)) {
        size_t header_and_load_cmds =
            header.sizeofcmds + MachHeaderSizeFromMagic(header.magic);
        if (header_and_load_cmds >= data_sp->GetByteSize()) {
          data_sp = MapFileData(file, header_and_load_cmds, file_offset);
          data.SetData(data_sp);
          data_offset = MachHeaderSizeFromMagic(header.magic);
        }
        if (data_sp) {
          ModuleSpec spec;
          spec.GetFileSpec() = file;
          spec.SetObjectOffset(file_offset);
          spec.SetObjectSize(length);
  
          spec.GetArchitecture() = GetArchitecture(header, data, data_offset);
          if (spec.GetArchitecture().IsValid()) {
            GetUUID(header, data, data_offset, spec.GetUUID());
            specs.Append(spec);
          }
        }
      }
    }
    return specs.GetSize() - initial_count;
  }
  
  ConstString ObjectFileMachO::GetSegmentNameTEXT() {
    static ConstString g_segment_name_TEXT("__TEXT");
    return g_segment_name_TEXT;
  }
  
  ConstString ObjectFileMachO::GetSegmentNameDATA() {
    static ConstString g_segment_name_DATA("__DATA");
    return g_segment_name_DATA;
  }
  
  ConstString ObjectFileMachO::GetSegmentNameDATA_DIRTY() {
    static ConstString g_segment_name("__DATA_DIRTY");
    return g_segment_name;
  }
  
  ConstString ObjectFileMachO::GetSegmentNameDATA_CONST() {
    static ConstString g_segment_name("__DATA_CONST");
    return g_segment_name;
  }
  
  ConstString ObjectFileMachO::GetSegmentNameOBJC() {
    static ConstString g_segment_name_OBJC("__OBJC");
    return g_segment_name_OBJC;
  }
  
  ConstString ObjectFileMachO::GetSegmentNameLINKEDIT() {
    static ConstString g_section_name_LINKEDIT("__LINKEDIT");
    return g_section_name_LINKEDIT;
  }
  
  ConstString ObjectFileMachO::GetSegmentNameDWARF() {
    static ConstString g_section_name("__DWARF");
    return g_section_name;
  }
  
  ConstString ObjectFileMachO::GetSectionNameEHFrame() {
    static ConstString g_section_name_eh_frame("__eh_frame");
    return g_section_name_eh_frame;
  }
  
  bool ObjectFileMachO::MagicBytesMatch(DataBufferSP &data_sp,
                                        lldb::addr_t data_offset,
                                        lldb::addr_t data_length) {
    DataExtractor data;
    data.SetData(data_sp, data_offset, data_length);
    lldb::offset_t offset = 0;
    uint32_t magic = data.GetU32(&offset);
    return MachHeaderSizeFromMagic(magic) != 0;
  }
  
  ObjectFileMachO::ObjectFileMachO(const lldb::ModuleSP &module_sp,
                                   DataBufferSP &data_sp,
                                   lldb::offset_t data_offset,
                                   const FileSpec *file,
                                   lldb::offset_t file_offset,
                                   lldb::offset_t length)
      : ObjectFile(module_sp, file, file_offset, length, data_sp, data_offset),
        m_mach_segments(), m_mach_sections(), m_entry_point_address(),
        m_thread_context_offsets(), m_thread_context_offsets_valid(false),
        m_reexported_dylibs(), m_allow_assembly_emulation_unwind_plans(true) {
    ::memset(&m_header, 0, sizeof(m_header));
    ::memset(&m_dysymtab, 0, sizeof(m_dysymtab));
  }
  
  ObjectFileMachO::ObjectFileMachO(const lldb::ModuleSP &module_sp,
                                   lldb::DataBufferSP &header_data_sp,
                                   const lldb::ProcessSP &process_sp,
                                   lldb::addr_t header_addr)
      : ObjectFile(module_sp, process_sp, header_addr, header_data_sp),
        m_mach_segments(), m_mach_sections(), m_entry_point_address(),
        m_thread_context_offsets(), m_thread_context_offsets_valid(false),
        m_reexported_dylibs(), m_allow_assembly_emulation_unwind_plans(true) {
    ::memset(&m_header, 0, sizeof(m_header));
    ::memset(&m_dysymtab, 0, sizeof(m_dysymtab));
  }
  
  bool ObjectFileMachO::ParseHeader(DataExtractor &data,
                                    lldb::offset_t *data_offset_ptr,
                                    llvm::MachO::mach_header &header) {
    data.SetByteOrder(endian::InlHostByteOrder());
    // Leave magic in the original byte order
    header.magic = data.GetU32(data_offset_ptr);
    bool can_parse = false;
    bool is_64_bit = false;
    switch (header.magic) {
    case MH_MAGIC:
      data.SetByteOrder(endian::InlHostByteOrder());
      data.SetAddressByteSize(4);
      can_parse = true;
      break;
  
    case MH_MAGIC_64:
      data.SetByteOrder(endian::InlHostByteOrder());
      data.SetAddressByteSize(8);
      can_parse = true;
      is_64_bit = true;
      break;
  
    case MH_CIGAM:
      data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
                            ? eByteOrderLittle
                            : eByteOrderBig);
      data.SetAddressByteSize(4);
      can_parse = true;
      break;
  
    case MH_CIGAM_64:
      data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
                            ? eByteOrderLittle
                            : eByteOrderBig);
      data.SetAddressByteSize(8);
      is_64_bit = true;
      can_parse = true;
      break;
  
    default:
      break;
    }
  
    if (can_parse) {
      data.GetU32(data_offset_ptr, &header.cputype, 6);
      if (is_64_bit)
        *data_offset_ptr += 4;
      return true;
    } else {
      memset(&header, 0, sizeof(header));
    }
    return false;
  }
  
  bool ObjectFileMachO::ParseHeader() {
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      bool can_parse = false;
      lldb::offset_t offset = 0;
      m_data.SetByteOrder(endian::InlHostByteOrder());
      // Leave magic in the original byte order
      m_header.magic = m_data.GetU32(&offset);
      switch (m_header.magic) {
      case MH_MAGIC:
        m_data.SetByteOrder(endian::InlHostByteOrder());
        m_data.SetAddressByteSize(4);
        can_parse = true;
        break;
  
      case MH_MAGIC_64:
        m_data.SetByteOrder(endian::InlHostByteOrder());
        m_data.SetAddressByteSize(8);
        can_parse = true;
        break;
  
      case MH_CIGAM:
        m_data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
                                ? eByteOrderLittle
                                : eByteOrderBig);
        m_data.SetAddressByteSize(4);
        can_parse = true;
        break;
  
      case MH_CIGAM_64:
        m_data.SetByteOrder(endian::InlHostByteOrder() == eByteOrderBig
                                ? eByteOrderLittle
                                : eByteOrderBig);
        m_data.SetAddressByteSize(8);
        can_parse = true;
        break;
  
      default:
        break;
      }
  
      if (can_parse) {
        m_data.GetU32(&offset, &m_header.cputype, 6);
  
        if (ArchSpec mach_arch = GetArchitecture()) {
          // Check if the module has a required architecture
          const ArchSpec &module_arch = module_sp->GetArchitecture();
          if (module_arch.IsValid() && !module_arch.IsCompatibleMatch(mach_arch))
            return false;
  
          if (SetModulesArchitecture(mach_arch)) {
            const size_t header_and_lc_size =
                m_header.sizeofcmds + MachHeaderSizeFromMagic(m_header.magic);
            if (m_data.GetByteSize() < header_and_lc_size) {
              DataBufferSP data_sp;
              ProcessSP process_sp(m_process_wp.lock());
              if (process_sp) {
                data_sp =
                    ReadMemory(process_sp, m_memory_addr, header_and_lc_size);
              } else {
                // Read in all only the load command data from the file on disk
                data_sp = MapFileData(m_file, header_and_lc_size, m_file_offset);
                if (data_sp->GetByteSize() != header_and_lc_size)
                  return false;
              }
              if (data_sp)
                m_data.SetData(data_sp);
            }
          }
          return true;
        }
      } else {
        memset(&m_header, 0, sizeof(struct mach_header));
      }
    }
    return false;
  }
  
  ByteOrder ObjectFileMachO::GetByteOrder() const {
    return m_data.GetByteOrder();
  }
  
  bool ObjectFileMachO::IsExecutable() const {
    return m_header.filetype == MH_EXECUTE;
  }
  
  uint32_t ObjectFileMachO::GetAddressByteSize() const {
    return m_data.GetAddressByteSize();
  }
  
  AddressClass ObjectFileMachO::GetAddressClass(lldb::addr_t file_addr) {
    Symtab *symtab = GetSymtab();
    if (symtab) {
      Symbol *symbol = symtab->FindSymbolContainingFileAddress(file_addr);
      if (symbol) {
        if (symbol->ValueIsAddress()) {
          SectionSP section_sp(symbol->GetAddressRef().GetSection());
          if (section_sp) {
            const lldb::SectionType section_type = section_sp->GetType();
            switch (section_type) {
            case eSectionTypeInvalid:
              return AddressClass::eUnknown;
  
            case eSectionTypeCode:
              if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM) {
                // For ARM we have a bit in the n_desc field of the symbol that
                // tells us ARM/Thumb which is bit 0x0008.
                if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB)
                  return AddressClass::eCodeAlternateISA;
              }
              return AddressClass::eCode;
  
            case eSectionTypeContainer:
              return AddressClass::eUnknown;
  
            case eSectionTypeData:
            case eSectionTypeDataCString:
            case eSectionTypeDataCStringPointers:
            case eSectionTypeDataSymbolAddress:
            case eSectionTypeData4:
            case eSectionTypeData8:
            case eSectionTypeData16:
            case eSectionTypeDataPointers:
            case eSectionTypeZeroFill:
            case eSectionTypeDataObjCMessageRefs:
            case eSectionTypeDataObjCCFStrings:
            case eSectionTypeGoSymtab:
              return AddressClass::eData;
  
            case eSectionTypeDebug:
            case eSectionTypeDWARFDebugAbbrev:
            case eSectionTypeDWARFDebugAbbrevDwo:
            case eSectionTypeDWARFDebugAddr:
            case eSectionTypeDWARFDebugAranges:
            case eSectionTypeDWARFDebugCuIndex:
            case eSectionTypeDWARFDebugFrame:
            case eSectionTypeDWARFDebugInfo:
            case eSectionTypeDWARFDebugInfoDwo:
            case eSectionTypeDWARFDebugLine:
            case eSectionTypeDWARFDebugLineStr:
            case eSectionTypeDWARFDebugLoc:
            case eSectionTypeDWARFDebugLocLists:
            case eSectionTypeDWARFDebugMacInfo:
            case eSectionTypeDWARFDebugMacro:
            case eSectionTypeDWARFDebugNames:
            case eSectionTypeDWARFDebugPubNames:
            case eSectionTypeDWARFDebugPubTypes:
            case eSectionTypeDWARFDebugRanges:
            case eSectionTypeDWARFDebugRngLists:
            case eSectionTypeDWARFDebugStr:
            case eSectionTypeDWARFDebugStrDwo:
            case eSectionTypeDWARFDebugStrOffsets:
            case eSectionTypeDWARFDebugStrOffsetsDwo:
            case eSectionTypeDWARFDebugTypes:
            case eSectionTypeDWARFAppleNames:
            case eSectionTypeDWARFAppleTypes:
            case eSectionTypeDWARFAppleExternalTypes:
            case eSectionTypeDWARFAppleNamespaces:
            case eSectionTypeDWARFAppleObjC:
            case eSectionTypeSwiftModules:
            case eSectionTypeDWARFGNUDebugAltLink:
              return AddressClass::eDebug;
  
            case eSectionTypeEHFrame:
            case eSectionTypeARMexidx:
            case eSectionTypeARMextab:
            case eSectionTypeCompactUnwind:
              return AddressClass::eRuntime;
  
            case eSectionTypeAbsoluteAddress:
            case eSectionTypeELFSymbolTable:
            case eSectionTypeELFDynamicSymbols:
            case eSectionTypeELFRelocationEntries:
            case eSectionTypeELFDynamicLinkInfo:
            case eSectionTypeOther:
              return AddressClass::eUnknown;
            }
          }
        }
  
        const SymbolType symbol_type = symbol->GetType();
        switch (symbol_type) {
        case eSymbolTypeAny:
          return AddressClass::eUnknown;
        case eSymbolTypeAbsolute:
          return AddressClass::eUnknown;
  
        case eSymbolTypeCode:
        case eSymbolTypeTrampoline:
        case eSymbolTypeResolver:
          if (m_header.cputype == llvm::MachO::CPU_TYPE_ARM) {
            // For ARM we have a bit in the n_desc field of the symbol that tells
            // us ARM/Thumb which is bit 0x0008.
            if (symbol->GetFlags() & MACHO_NLIST_ARM_SYMBOL_IS_THUMB)
              return AddressClass::eCodeAlternateISA;
          }
          return AddressClass::eCode;
  
        case eSymbolTypeData:
          return AddressClass::eData;
        case eSymbolTypeRuntime:
          return AddressClass::eRuntime;
        case eSymbolTypeException:
          return AddressClass::eRuntime;
        case eSymbolTypeSourceFile:
          return AddressClass::eDebug;
        case eSymbolTypeHeaderFile:
          return AddressClass::eDebug;
        case eSymbolTypeObjectFile:
          return AddressClass::eDebug;
        case eSymbolTypeCommonBlock:
          return AddressClass::eDebug;
        case eSymbolTypeBlock:
          return AddressClass::eDebug;
        case eSymbolTypeLocal:
          return AddressClass::eData;
        case eSymbolTypeParam:
          return AddressClass::eData;
        case eSymbolTypeVariable:
          return AddressClass::eData;
        case eSymbolTypeVariableType:
          return AddressClass::eDebug;
        case eSymbolTypeLineEntry:
          return AddressClass::eDebug;
        case eSymbolTypeLineHeader:
          return AddressClass::eDebug;
        case eSymbolTypeScopeBegin:
          return AddressClass::eDebug;
        case eSymbolTypeScopeEnd:
          return AddressClass::eDebug;
        case eSymbolTypeAdditional:
          return AddressClass::eUnknown;
        case eSymbolTypeCompiler:
          return AddressClass::eDebug;
        case eSymbolTypeInstrumentation:
          return AddressClass::eDebug;
        case eSymbolTypeUndefined:
          return AddressClass::eUnknown;
        case eSymbolTypeObjCClass:
          return AddressClass::eRuntime;
        case eSymbolTypeObjCMetaClass:
          return AddressClass::eRuntime;
        case eSymbolTypeObjCIVar:
          return AddressClass::eRuntime;
        case eSymbolTypeReExported:
          return AddressClass::eRuntime;
        case eSymbolTypeASTFile:
          return AddressClass::eDebug;
  
        case eSymbolTypeIVarOffset:
          break;
        }
      }
    }
    return AddressClass::eUnknown;
  }
  
  Symtab *ObjectFileMachO::GetSymtab() {
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      if (m_symtab_up == NULL) {
        m_symtab_up.reset(new Symtab(this));
        std::lock_guard<std::recursive_mutex> symtab_guard(
            m_symtab_up->GetMutex());
        ParseSymtab();
        m_symtab_up->Finalize();
      }
    }
    return m_symtab_up.get();
  }
  
  bool ObjectFileMachO::IsStripped() {
    if (m_dysymtab.cmd == 0) {
      ModuleSP module_sp(GetModule());
      if (module_sp) {
        lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
        for (uint32_t i = 0; i < m_header.ncmds; ++i) {
          const lldb::offset_t load_cmd_offset = offset;
  
          load_command lc;
          if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
            break;
          if (lc.cmd == LC_DYSYMTAB) {
            m_dysymtab.cmd = lc.cmd;
            m_dysymtab.cmdsize = lc.cmdsize;
            if (m_data.GetU32(&offset, &m_dysymtab.ilocalsym,
                              (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2) ==
                NULL) {
              // Clear m_dysymtab if we were unable to read all items from the
              // load command
              ::memset(&m_dysymtab, 0, sizeof(m_dysymtab));
            }
          }
          offset = load_cmd_offset + lc.cmdsize;
        }
      }
    }
    if (m_dysymtab.cmd)
      return m_dysymtab.nlocalsym <= 1;
    return false;
  }
  
  ObjectFileMachO::EncryptedFileRanges ObjectFileMachO::GetEncryptedFileRanges() {
    EncryptedFileRanges result;
    lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
  
    encryption_info_command encryption_cmd;
    for (uint32_t i = 0; i < m_header.ncmds; ++i) {
      const lldb::offset_t load_cmd_offset = offset;
      if (m_data.GetU32(&offset, &encryption_cmd, 2) == NULL)
        break;
  
      // LC_ENCRYPTION_INFO and LC_ENCRYPTION_INFO_64 have the same sizes for the
      // 3 fields we care about, so treat them the same.
      if (encryption_cmd.cmd == LC_ENCRYPTION_INFO ||
          encryption_cmd.cmd == LC_ENCRYPTION_INFO_64) {
        if (m_data.GetU32(&offset, &encryption_cmd.cryptoff, 3)) {
          if (encryption_cmd.cryptid != 0) {
            EncryptedFileRanges::Entry entry;
            entry.SetRangeBase(encryption_cmd.cryptoff);
            entry.SetByteSize(encryption_cmd.cryptsize);
            result.Append(entry);
          }
        }
      }
      offset = load_cmd_offset + encryption_cmd.cmdsize;
    }
  
    return result;
  }
  
  void ObjectFileMachO::SanitizeSegmentCommand(segment_command_64 &seg_cmd,
                                               uint32_t cmd_idx) {
    if (m_length == 0 || seg_cmd.filesize == 0)
      return;
  
    if (seg_cmd.fileoff > m_length) {
      // We have a load command that says it extends past the end of the file.
      // This is likely a corrupt file.  We don't have any way to return an error
      // condition here (this method was likely invoked from something like
      // ObjectFile::GetSectionList()), so we just null out the section contents,
      // and dump a message to stdout.  The most common case here is core file
      // debugging with a truncated file.
      const char *lc_segment_name =
          seg_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT";
      GetModule()->ReportWarning(
          "load command %u %s has a fileoff (0x%" PRIx64
          ") that extends beyond the end of the file (0x%" PRIx64
          "), ignoring this section",
          cmd_idx, lc_segment_name, seg_cmd.fileoff, m_length);
  
      seg_cmd.fileoff = 0;
      seg_cmd.filesize = 0;
    }
  
    if (seg_cmd.fileoff + seg_cmd.filesize > m_length) {
      // We have a load command that says it extends past the end of the file.
      // This is likely a corrupt file.  We don't have any way to return an error
      // condition here (this method was likely invoked from something like
      // ObjectFile::GetSectionList()), so we just null out the section contents,
      // and dump a message to stdout.  The most common case here is core file
      // debugging with a truncated file.
      const char *lc_segment_name =
          seg_cmd.cmd == LC_SEGMENT_64 ? "LC_SEGMENT_64" : "LC_SEGMENT";
      GetModule()->ReportWarning(
          "load command %u %s has a fileoff + filesize (0x%" PRIx64
          ") that extends beyond the end of the file (0x%" PRIx64
          "), the segment will be truncated to match",
          cmd_idx, lc_segment_name, seg_cmd.fileoff + seg_cmd.filesize, m_length);
  
      // Truncate the length
      seg_cmd.filesize = m_length - seg_cmd.fileoff;
    }
  }
  
  static uint32_t GetSegmentPermissions(const segment_command_64 &seg_cmd) {
    uint32_t result = 0;
    if (seg_cmd.initprot & VM_PROT_READ)
      result |= ePermissionsReadable;
    if (seg_cmd.initprot & VM_PROT_WRITE)
      result |= ePermissionsWritable;
    if (seg_cmd.initprot & VM_PROT_EXECUTE)
      result |= ePermissionsExecutable;
    return result;
  }
  
  static lldb::SectionType GetSectionType(uint32_t flags,
                                          ConstString section_name) {
  
    if (flags & (S_ATTR_PURE_INSTRUCTIONS | S_ATTR_SOME_INSTRUCTIONS))
      return eSectionTypeCode;
  
    uint32_t mach_sect_type = flags & SECTION_TYPE;
    static ConstString g_sect_name_objc_data("__objc_data");
    static ConstString g_sect_name_objc_msgrefs("__objc_msgrefs");
    static ConstString g_sect_name_objc_selrefs("__objc_selrefs");
    static ConstString g_sect_name_objc_classrefs("__objc_classrefs");
    static ConstString g_sect_name_objc_superrefs("__objc_superrefs");
    static ConstString g_sect_name_objc_const("__objc_const");
    static ConstString g_sect_name_objc_classlist("__objc_classlist");
    static ConstString g_sect_name_cfstring("__cfstring");
  
    static ConstString g_sect_name_dwarf_debug_abbrev("__debug_abbrev");
    static ConstString g_sect_name_dwarf_debug_aranges("__debug_aranges");
    static ConstString g_sect_name_dwarf_debug_frame("__debug_frame");
    static ConstString g_sect_name_dwarf_debug_info("__debug_info");
    static ConstString g_sect_name_dwarf_debug_line("__debug_line");
    static ConstString g_sect_name_dwarf_debug_loc("__debug_loc");
    static ConstString g_sect_name_dwarf_debug_loclists("__debug_loclists");
    static ConstString g_sect_name_dwarf_debug_macinfo("__debug_macinfo");
    static ConstString g_sect_name_dwarf_debug_names("__debug_names");
    static ConstString g_sect_name_dwarf_debug_pubnames("__debug_pubnames");
    static ConstString g_sect_name_dwarf_debug_pubtypes("__debug_pubtypes");
    static ConstString g_sect_name_dwarf_debug_ranges("__debug_ranges");
    static ConstString g_sect_name_dwarf_debug_str("__debug_str");
    static ConstString g_sect_name_dwarf_debug_types("__debug_types");
    static ConstString g_sect_name_dwarf_apple_names("__apple_names");
    static ConstString g_sect_name_dwarf_apple_types("__apple_types");
    static ConstString g_sect_name_dwarf_apple_namespaces("__apple_namespac");
    static ConstString g_sect_name_dwarf_apple_objc("__apple_objc");
    static ConstString g_sect_name_eh_frame("__eh_frame");
    static ConstString g_sect_name_compact_unwind("__unwind_info");
    static ConstString g_sect_name_text("__text");
    static ConstString g_sect_name_data("__data");
    static ConstString g_sect_name_swift_ast("__swift_ast");
    static ConstString g_sect_name_go_symtab("__gosymtab");
  
    if (section_name == g_sect_name_dwarf_debug_abbrev)
      return eSectionTypeDWARFDebugAbbrev;
    if (section_name == g_sect_name_dwarf_debug_aranges)
      return eSectionTypeDWARFDebugAranges;
    if (section_name == g_sect_name_dwarf_debug_frame)
      return eSectionTypeDWARFDebugFrame;
    if (section_name == g_sect_name_dwarf_debug_info)
      return eSectionTypeDWARFDebugInfo;
    if (section_name == g_sect_name_dwarf_debug_line)
      return eSectionTypeDWARFDebugLine;
    if (section_name == g_sect_name_dwarf_debug_loc)
      return eSectionTypeDWARFDebugLoc;
    if (section_name == g_sect_name_dwarf_debug_loclists)
      return eSectionTypeDWARFDebugLocLists;
    if (section_name == g_sect_name_dwarf_debug_macinfo)
      return eSectionTypeDWARFDebugMacInfo;
    if (section_name == g_sect_name_dwarf_debug_names)
      return eSectionTypeDWARFDebugNames;
    if (section_name == g_sect_name_dwarf_debug_pubnames)
      return eSectionTypeDWARFDebugPubNames;
    if (section_name == g_sect_name_dwarf_debug_pubtypes)
      return eSectionTypeDWARFDebugPubTypes;
    if (section_name == g_sect_name_dwarf_debug_ranges)
      return eSectionTypeDWARFDebugRanges; 
    if (section_name == g_sect_name_dwarf_debug_str)
      return eSectionTypeDWARFDebugStr;
    if (section_name == g_sect_name_dwarf_debug_types)
      return eSectionTypeDWARFDebugTypes;
    if (section_name == g_sect_name_dwarf_apple_names)
      return eSectionTypeDWARFAppleNames;
    if (section_name == g_sect_name_dwarf_apple_types)
      return eSectionTypeDWARFAppleTypes;
    if (section_name == g_sect_name_dwarf_apple_namespaces)
      return eSectionTypeDWARFAppleNamespaces;
    if (section_name == g_sect_name_dwarf_apple_objc)
      return eSectionTypeDWARFAppleObjC;
    if (section_name == g_sect_name_objc_selrefs)
      return eSectionTypeDataCStringPointers;
    if (section_name == g_sect_name_objc_msgrefs)
      return eSectionTypeDataObjCMessageRefs;
    if (section_name == g_sect_name_eh_frame)
      return eSectionTypeEHFrame;
    if (section_name == g_sect_name_compact_unwind)
      return eSectionTypeCompactUnwind;
    if (section_name == g_sect_name_cfstring)
      return eSectionTypeDataObjCCFStrings;
    if (section_name == g_sect_name_swift_ast)
      return eSectionTypeSwiftModules;
    if (section_name == g_sect_name_go_symtab)
      return eSectionTypeGoSymtab;
    if (section_name == g_sect_name_objc_data ||
        section_name == g_sect_name_objc_classrefs ||
        section_name == g_sect_name_objc_superrefs ||
        section_name == g_sect_name_objc_const ||
        section_name == g_sect_name_objc_classlist) {
      return eSectionTypeDataPointers;
    }
  
    switch (mach_sect_type) {
    // TODO: categorize sections by other flags for regular sections
    case S_REGULAR:
      if (section_name == g_sect_name_text)
        return eSectionTypeCode;
      if (section_name == g_sect_name_data)
        return eSectionTypeData;
      return eSectionTypeOther;
    case S_ZEROFILL:
      return eSectionTypeZeroFill;
    case S_CSTRING_LITERALS: // section with only literal C strings
      return eSectionTypeDataCString;
    case S_4BYTE_LITERALS: // section with only 4 byte literals
      return eSectionTypeData4;
    case S_8BYTE_LITERALS: // section with only 8 byte literals
      return eSectionTypeData8;
    case S_LITERAL_POINTERS: // section with only pointers to literals
      return eSectionTypeDataPointers;
    case S_NON_LAZY_SYMBOL_POINTERS: // section with only non-lazy symbol pointers
      return eSectionTypeDataPointers;
    case S_LAZY_SYMBOL_POINTERS: // section with only lazy symbol pointers
      return eSectionTypeDataPointers;
    case S_SYMBOL_STUBS: // section with only symbol stubs, byte size of stub in
                         // the reserved2 field
      return eSectionTypeCode;
    case S_MOD_INIT_FUNC_POINTERS: // section with only function pointers for
                                   // initialization
      return eSectionTypeDataPointers;
    case S_MOD_TERM_FUNC_POINTERS: // section with only function pointers for
                                   // termination
      return eSectionTypeDataPointers;
    case S_COALESCED:
      return eSectionTypeOther;
    case S_GB_ZEROFILL:
      return eSectionTypeZeroFill;
    case S_INTERPOSING: // section with only pairs of function pointers for
                        // interposing
      return eSectionTypeCode;
    case S_16BYTE_LITERALS: // section with only 16 byte literals
      return eSectionTypeData16;
    case S_DTRACE_DOF:
      return eSectionTypeDebug;
    case S_LAZY_DYLIB_SYMBOL_POINTERS:
      return eSectionTypeDataPointers;
    default:
      return eSectionTypeOther;
    }
  }
  
  struct ObjectFileMachO::SegmentParsingContext {
    const EncryptedFileRanges EncryptedRanges;
    lldb_private::SectionList &UnifiedList;
    uint32_t NextSegmentIdx = 0;
    uint32_t NextSectionIdx = 0;
    bool FileAddressesChanged = false;
  
    SegmentParsingContext(EncryptedFileRanges EncryptedRanges,
                          lldb_private::SectionList &UnifiedList)
        : EncryptedRanges(std::move(EncryptedRanges)), UnifiedList(UnifiedList) {}
  };
  
  void ObjectFileMachO::ProcessSegmentCommand(const load_command &load_cmd_,
                                              lldb::offset_t offset,
                                              uint32_t cmd_idx,
                                              SegmentParsingContext &context) {
    segment_command_64 load_cmd;
    memcpy(&load_cmd, &load_cmd_, sizeof(load_cmd_));
  
    if (!m_data.GetU8(&offset, (uint8_t *)load_cmd.segname, 16))
      return;
  
    ModuleSP module_sp = GetModule();
    const bool is_core = GetType() == eTypeCoreFile;
    const bool is_dsym = (m_header.filetype == MH_DSYM);
    bool add_section = true;
    bool add_to_unified = true;
    ConstString const_segname(
        load_cmd.segname,
        std::min<size_t>(strlen(load_cmd.segname), sizeof(load_cmd.segname)));
  
    SectionSP unified_section_sp(
        context.UnifiedList.FindSectionByName(const_segname));
    if (is_dsym && unified_section_sp) {
      if (const_segname == GetSegmentNameLINKEDIT()) {
        // We need to keep the __LINKEDIT segment private to this object file
        // only
        add_to_unified = false;
      } else {
        // This is the dSYM file and this section has already been created by the
        // object file, no need to create it.
        add_section = false;
      }
    }
    load_cmd.vmaddr = m_data.GetAddress(&offset);
    load_cmd.vmsize = m_data.GetAddress(&offset);
    load_cmd.fileoff = m_data.GetAddress(&offset);
    load_cmd.filesize = m_data.GetAddress(&offset);
    if (!m_data.GetU32(&offset, &load_cmd.maxprot, 4))
      return;
  
    SanitizeSegmentCommand(load_cmd, cmd_idx);
  
    const uint32_t segment_permissions = GetSegmentPermissions(load_cmd);
    const bool segment_is_encrypted =
        (load_cmd.flags & SG_PROTECTED_VERSION_1) != 0;
  
    // Keep a list of mach segments around in case we need to get at data that
    // isn't stored in the abstracted Sections.
    m_mach_segments.push_back(load_cmd);
  
    // Use a segment ID of the segment index shifted left by 8 so they never
    // conflict with any of the sections.
    SectionSP segment_sp;
    if (add_section && (const_segname || is_core)) {
      segment_sp = std::make_shared<Section>(
          module_sp, // Module to which this section belongs
          this,      // Object file to which this sections belongs
          ++context.NextSegmentIdx
              << 8, // Section ID is the 1 based segment index
          // shifted right by 8 bits as not to collide with any of the 256
          // section IDs that are possible
          const_segname,         // Name of this section
          eSectionTypeContainer, // This section is a container of other
          // sections.
          load_cmd.vmaddr, // File VM address == addresses as they are
          // found in the object file
          load_cmd.vmsize,  // VM size in bytes of this section
          load_cmd.fileoff, // Offset to the data for this section in
          // the file
          load_cmd.filesize, // Size in bytes of this section as found
          // in the file
          0,                // Segments have no alignment information
          load_cmd.flags); // Flags for this section
  
      segment_sp->SetIsEncrypted(segment_is_encrypted);
      m_sections_up->AddSection(segment_sp);
      segment_sp->SetPermissions(segment_permissions);
      if (add_to_unified)
        context.UnifiedList.AddSection(segment_sp);
    } else if (unified_section_sp) {
      if (is_dsym && unified_section_sp->GetFileAddress() != load_cmd.vmaddr) {
        // Check to see if the module was read from memory?
        if (module_sp->GetObjectFile()->GetBaseAddress().IsValid()) {
          // We have a module that is in memory and needs to have its file
          // address adjusted. We need to do this because when we load a file
          // from memory, its addresses will be slid already, yet the addresses
          // in the new symbol file will still be unslid.  Since everything is
          // stored as section offset, this shouldn't cause any problems.
  
          // Make sure we've parsed the symbol table from the ObjectFile before
          // we go around changing its Sections.
          module_sp->GetObjectFile()->GetSymtab();
          // eh_frame would present the same problems but we parse that on a per-
          // function basis as-needed so it's more difficult to remove its use of
          // the Sections.  Realistically, the environments where this code path
          // will be taken will not have eh_frame sections.
  
          unified_section_sp->SetFileAddress(load_cmd.vmaddr);
  
          // Notify the module that the section addresses have been changed once
          // we're done so any file-address caches can be updated.
          context.FileAddressesChanged = true;
        }
      }
      m_sections_up->AddSection(unified_section_sp);
    }
  
    struct section_64 sect64;
    ::memset(&sect64, 0, sizeof(sect64));
    // Push a section into our mach sections for the section at index zero
    // (NO_SECT) if we don't have any mach sections yet...
    if (m_mach_sections.empty())
      m_mach_sections.push_back(sect64);
    uint32_t segment_sect_idx;
    const lldb::user_id_t first_segment_sectID = context.NextSectionIdx + 1;
  
    const uint32_t num_u32s = load_cmd.cmd == LC_SEGMENT ? 7 : 8;
    for (segment_sect_idx = 0; segment_sect_idx < load_cmd.nsects;
         ++segment_sect_idx) {
      if (m_data.GetU8(&offset, (uint8_t *)sect64.sectname,
                       sizeof(sect64.sectname)) == NULL)
        break;
      if (m_data.GetU8(&offset, (uint8_t *)sect64.segname,
                       sizeof(sect64.segname)) == NULL)
        break;
      sect64.addr = m_data.GetAddress(&offset);
      sect64.size = m_data.GetAddress(&offset);
  
      if (m_data.GetU32(&offset, &sect64.offset, num_u32s) == NULL)
        break;
  
      // Keep a list of mach sections around in case we need to get at data that
      // isn't stored in the abstracted Sections.
      m_mach_sections.push_back(sect64);
  
      if (add_section) {
        ConstString section_name(
            sect64.sectname,
            std::min<size_t>(strlen(sect64.sectname), sizeof(sect64.sectname)));
        if (!const_segname) {
          // We have a segment with no name so we need to conjure up segments
          // that correspond to the section's segname if there isn't already such
          // a section. If there is such a section, we resize the section so that
          // it spans all sections.  We also mark these sections as fake so
          // address matches don't hit if they land in the gaps between the child
          // sections.
          const_segname.SetTrimmedCStringWithLength(sect64.segname,
                                                    sizeof(sect64.segname));
          segment_sp = context.UnifiedList.FindSectionByName(const_segname);
          if (segment_sp.get()) {
            Section *segment = segment_sp.get();
            // Grow the section size as needed.
            const lldb::addr_t sect64_min_addr = sect64.addr;
            const lldb::addr_t sect64_max_addr = sect64_min_addr + sect64.size;
            const lldb::addr_t curr_seg_byte_size = segment->GetByteSize();
            const lldb::addr_t curr_seg_min_addr = segment->GetFileAddress();
            const lldb::addr_t curr_seg_max_addr =
                curr_seg_min_addr + curr_seg_byte_size;
            if (sect64_min_addr >= curr_seg_min_addr) {
              const lldb::addr_t new_seg_byte_size =
                  sect64_max_addr - curr_seg_min_addr;
              // Only grow the section size if needed
              if (new_seg_byte_size > curr_seg_byte_size)
                segment->SetByteSize(new_seg_byte_size);
            } else {
              // We need to change the base address of the segment and adjust the
              // child section offsets for all existing children.
              const lldb::addr_t slide_amount =
                  sect64_min_addr - curr_seg_min_addr;
              segment->Slide(slide_amount, false);
              segment->GetChildren().Slide(-slide_amount, false);
              segment->SetByteSize(curr_seg_max_addr - sect64_min_addr);
            }
  
            // Grow the section size as needed.
            if (sect64.offset) {
              const lldb::addr_t segment_min_file_offset =
                  segment->GetFileOffset();
              const lldb::addr_t segment_max_file_offset =
                  segment_min_file_offset + segment->GetFileSize();
  
              const lldb::addr_t section_min_file_offset = sect64.offset;
              const lldb::addr_t section_max_file_offset =
                  section_min_file_offset + sect64.size;
              const lldb::addr_t new_file_offset =
                  std::min(section_min_file_offset, segment_min_file_offset);
              const lldb::addr_t new_file_size =
                  std::max(section_max_file_offset, segment_max_file_offset) -
                  new_file_offset;
              segment->SetFileOffset(new_file_offset);
              segment->SetFileSize(new_file_size);
            }
          } else {
            // Create a fake section for the section's named segment
            segment_sp = std::make_shared<Section>(
                segment_sp, // Parent section
                module_sp,  // Module to which this section belongs
                this,       // Object file to which this section belongs
                ++context.NextSegmentIdx
                    << 8, // Section ID is the 1 based segment index
                // shifted right by 8 bits as not to
                // collide with any of the 256 section IDs
                // that are possible
                const_segname,         // Name of this section
                eSectionTypeContainer, // This section is a container of
                // other sections.
                sect64.addr, // File VM address == addresses as they are
                // found in the object file
                sect64.size,   // VM size in bytes of this section
                sect64.offset, // Offset to the data for this section in
                // the file
                sect64.offset ? sect64.size : 0, // Size in bytes of
                // this section as
                // found in the file
                sect64.align,
                load_cmd.flags); // Flags for this section
            segment_sp->SetIsFake(true);
            segment_sp->SetPermissions(segment_permissions);
            m_sections_up->AddSection(segment_sp);
            if (add_to_unified)
              context.UnifiedList.AddSection(segment_sp);
            segment_sp->SetIsEncrypted(segment_is_encrypted);
          }
        }
        assert(segment_sp.get());
  
        lldb::SectionType sect_type = GetSectionType(sect64.flags, section_name);
  
        SectionSP section_sp(new Section(
            segment_sp, module_sp, this, ++context.NextSectionIdx, section_name,
            sect_type, sect64.addr - segment_sp->GetFileAddress(), sect64.size,
            sect64.offset, sect64.offset == 0 ? 0 : sect64.size, sect64.align,
            sect64.flags));
        // Set the section to be encrypted to match the segment
  
        bool section_is_encrypted = false;
        if (!segment_is_encrypted && load_cmd.filesize != 0)
          section_is_encrypted = context.EncryptedRanges.FindEntryThatContains(
                                     sect64.offset) != NULL;
  
        section_sp->SetIsEncrypted(segment_is_encrypted || section_is_encrypted);
        section_sp->SetPermissions(segment_permissions);
        segment_sp->GetChildren().AddSection(section_sp);
  
        if (segment_sp->IsFake()) {
          segment_sp.reset();
          const_segname.Clear();
        }
      }
    }
    if (segment_sp && is_dsym) {
      if (first_segment_sectID <= context.NextSectionIdx) {
        lldb::user_id_t sect_uid;
        for (sect_uid = first_segment_sectID; sect_uid <= context.NextSectionIdx;
             ++sect_uid) {
          SectionSP curr_section_sp(
              segment_sp->GetChildren().FindSectionByID(sect_uid));
          SectionSP next_section_sp;
          if (sect_uid + 1 <= context.NextSectionIdx)
            next_section_sp =
                segment_sp->GetChildren().FindSectionByID(sect_uid + 1);
  
          if (curr_section_sp.get()) {
            if (curr_section_sp->GetByteSize() == 0) {
              if (next_section_sp.get() != NULL)
                curr_section_sp->SetByteSize(next_section_sp->GetFileAddress() -
                                             curr_section_sp->GetFileAddress());
              else
                curr_section_sp->SetByteSize(load_cmd.vmsize);
            }
          }
        }
      }
    }
  }
  
  void ObjectFileMachO::ProcessDysymtabCommand(const load_command &load_cmd,
                                               lldb::offset_t offset) {
    m_dysymtab.cmd = load_cmd.cmd;
    m_dysymtab.cmdsize = load_cmd.cmdsize;
    m_data.GetU32(&offset, &m_dysymtab.ilocalsym,
                  (sizeof(m_dysymtab) / sizeof(uint32_t)) - 2);
  }
  
  void ObjectFileMachO::CreateSections(SectionList &unified_section_list) {
    if (m_sections_up)
      return;
  
    m_sections_up.reset(new SectionList());
  
    lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
    // bool dump_sections = false;
    ModuleSP module_sp(GetModule());
  
    offset = MachHeaderSizeFromMagic(m_header.magic);
  
    SegmentParsingContext context(GetEncryptedFileRanges(), unified_section_list);
    struct load_command load_cmd;
    for (uint32_t i = 0; i < m_header.ncmds; ++i) {
      const lldb::offset_t load_cmd_offset = offset;
      if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
        break;
  
      if (load_cmd.cmd == LC_SEGMENT || load_cmd.cmd == LC_SEGMENT_64)
        ProcessSegmentCommand(load_cmd, offset, i, context);
      else if (load_cmd.cmd == LC_DYSYMTAB)
        ProcessDysymtabCommand(load_cmd, offset);
  
      offset = load_cmd_offset + load_cmd.cmdsize;
    }
  
    if (context.FileAddressesChanged && module_sp)
      module_sp->SectionFileAddressesChanged();
  }
  
  class MachSymtabSectionInfo {
  public:
    MachSymtabSectionInfo(SectionList *section_list)
        : m_section_list(section_list), m_section_infos() {
      // Get the number of sections down to a depth of 1 to include all segments
      // and their sections, but no other sections that may be added for debug
      // map or
      m_section_infos.resize(section_list->GetNumSections(1));
    }
  
    SectionSP GetSection(uint8_t n_sect, addr_t file_addr) {
      if (n_sect == 0)
        return SectionSP();
      if (n_sect < m_section_infos.size()) {
        if (!m_section_infos[n_sect].section_sp) {
          SectionSP section_sp(m_section_list->FindSectionByID(n_sect));
          m_section_infos[n_sect].section_sp = section_sp;
          if (section_sp) {
            m_section_infos[n_sect].vm_range.SetBaseAddress(
                section_sp->GetFileAddress());
            m_section_infos[n_sect].vm_range.SetByteSize(
                section_sp->GetByteSize());
          } else {
            Host::SystemLog(Host::eSystemLogError,
                            "error: unable to find section for section %u\n",
                            n_sect);
          }
        }
        if (m_section_infos[n_sect].vm_range.Contains(file_addr)) {
          // Symbol is in section.
          return m_section_infos[n_sect].section_sp;
        } else if (m_section_infos[n_sect].vm_range.GetByteSize() == 0 &&
                   m_section_infos[n_sect].vm_range.GetBaseAddress() ==
                       file_addr) {
          // Symbol is in section with zero size, but has the same start address
          // as the section. This can happen with linker symbols (symbols that
          // start with the letter 'l' or 'L'.
          return m_section_infos[n_sect].section_sp;
        }
      }
      return m_section_list->FindSectionContainingFileAddress(file_addr);
    }
  
  protected:
    struct SectionInfo {
      SectionInfo() : vm_range(), section_sp() {}
  
      VMRange vm_range;
      SectionSP section_sp;
    };
    SectionList *m_section_list;
    std::vector<SectionInfo> m_section_infos;
  };
  
  struct TrieEntry {
    TrieEntry()
        : name(), address(LLDB_INVALID_ADDRESS), flags(0), other(0),
          import_name() {}
  
    void Clear() {
      name.Clear();
      address = LLDB_INVALID_ADDRESS;
      flags = 0;
      other = 0;
      import_name.Clear();
    }
  
    void Dump() const {
      printf("0x%16.16llx 0x%16.16llx 0x%16.16llx \"%s\"",
             static_cast<unsigned long long>(address),
             static_cast<unsigned long long>(flags),
             static_cast<unsigned long long>(other), name.GetCString());
      if (import_name)
        printf(" -> \"%s\"\n", import_name.GetCString());
      else
        printf("\n");
    }
    ConstString name;
    uint64_t address;
    uint64_t flags;
    uint64_t other;
    ConstString import_name;
  };
  
  struct TrieEntryWithOffset {
    lldb::offset_t nodeOffset;
    TrieEntry entry;
  
    TrieEntryWithOffset(lldb::offset_t offset) : nodeOffset(offset), entry() {}
  
    void Dump(uint32_t idx) const {
      printf("[%3u] 0x%16.16llx: ", idx,
             static_cast<unsigned long long>(nodeOffset));
      entry.Dump();
    }
  
    bool operator<(const TrieEntryWithOffset &other) const {
      return (nodeOffset < other.nodeOffset);
    }
  };
  
  static LazyBool CalculateNameIsSwift(std::vector<llvm::StringRef> &nameSlices) {
    // Currently a symbol is defined to possibly be in the Trie only if
    // it is a swift symbol. Swift mangled names start with "__T" so we
    // need to see if the nameSlices start with "__T", but of course
    // this could be broken up into at most 3 entries.
  
    llvm::StringRef swift_prefix("__T");
  
    for (const auto &name : nameSlices) {
      const size_t name_len = name.size();
      const size_t swift_prefix_len =
          swift_prefix.size(); // This length changes as we trim it down so we
                               // must always fetch it
      if (swift_prefix_len > name_len) {
        // The remaining swift prefix is longer than the current
        // name slice, so if the prefix starts with the name, then
        // trim characters off the swift prefix and keep looking,
        // else we are done if the swift prefix doesn't start with
        // the name
        if (swift_prefix.startswith(name))
          swift_prefix = swift_prefix.substr(name_len);
        else
          return eLazyBoolNo;
      } else {
        // The current name slice is greater than or equal to
        // the remaining prefix, so just test if it starts with
        // the prefix and we are done
        if (name.startswith(swift_prefix))
          return eLazyBoolYes;
        else
          return eLazyBoolNo;
      }
    }
    return eLazyBoolCalculate;
  }
  
  static bool ParseTrieEntries(DataExtractor &data, lldb::offset_t offset,
                               const bool is_arm, const LazyBool symbol_is_swift,
                               std::vector<llvm::StringRef> &nameSlices,
                               std::set<lldb::addr_t> &resolver_addresses,
                               std::vector<TrieEntryWithOffset> &output) {
    if (!data.ValidOffset(offset))
      return true;
  
    const uint64_t terminalSize = data.GetULEB128(&offset);
    lldb::offset_t children_offset = offset + terminalSize;
    if (terminalSize != 0) {
      TrieEntryWithOffset e(offset);
      e.entry.flags = data.GetULEB128(&offset);
      const char *import_name = NULL;
      if (e.entry.flags & EXPORT_SYMBOL_FLAGS_REEXPORT) {
        e.entry.address = 0;
        e.entry.other = data.GetULEB128(&offset); // dylib ordinal
        import_name = data.GetCStr(&offset);
      } else {
        e.entry.address = data.GetULEB128(&offset);
        if (e.entry.flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER) {
          e.entry.other = data.GetULEB128(&offset);
          uint64_t resolver_addr = e.entry.other;
          if (is_arm)
            resolver_addr &= THUMB_ADDRESS_BIT_MASK;
          resolver_addresses.insert(resolver_addr);
        } else
          e.entry.other = 0;
      }
      // Only add symbols that are reexport symbols with a valid import name
      if ((EXPORT_SYMBOL_FLAGS_REEXPORT & e.entry.flags && import_name &&
           import_name[0]) ||
          symbol_is_swift == eLazyBoolYes) {
        std::string name;
        if (!nameSlices.empty()) {
          for (auto name_slice : nameSlices)
            name.append(name_slice.data(), name_slice.size());
        }
        if (name.size() > 1) {
          // Skip the leading '_'
          e.entry.name.SetCStringWithLength(name.c_str() + 1, name.size() - 1);
        }
        if (import_name) {
          // Skip the leading '_'
          e.entry.import_name.SetCString(import_name + 1);
        }
        output.push_back(e);
      }
    }
  
    const uint8_t childrenCount = data.GetU8(&children_offset);
    for (uint8_t i = 0; i < childrenCount; ++i) {
      const char *cstr = data.GetCStr(&children_offset);
      if (cstr)
        nameSlices.push_back(llvm::StringRef(cstr));
      else
        return false; // Corrupt data
  
      const LazyBool child_symbol_is_swift =
          (symbol_is_swift == eLazyBoolCalculate)
              ? CalculateNameIsSwift(nameSlices)
              : symbol_is_swift;
  
      lldb::offset_t childNodeOffset = data.GetULEB128(&children_offset);
      if (childNodeOffset) {
        if (!ParseTrieEntries(data, childNodeOffset, is_arm,
                              child_symbol_is_swift, nameSlices,
                              resolver_addresses, output)) {
          return false;
        }
      }
      nameSlices.pop_back();
    }
    return true;
  }
  
  // Read the UUID out of a dyld_shared_cache file on-disk.
  UUID ObjectFileMachO::GetSharedCacheUUID(FileSpec dyld_shared_cache,
                                           const ByteOrder byte_order,
                                           const uint32_t addr_byte_size) {
    UUID dsc_uuid;
    DataBufferSP DscData = MapFileData(
        dyld_shared_cache, sizeof(struct lldb_copy_dyld_cache_header_v1), 0);
    if (!DscData)
      return dsc_uuid;
    DataExtractor dsc_header_data(DscData, byte_order, addr_byte_size);
  
    char version_str[7];
    lldb::offset_t offset = 0;
    memcpy(version_str, dsc_header_data.GetData(&offset, 6), 6);
    version_str[6] = '\0';
    if (strcmp(version_str, "dyld_v") == 0) {
      offset = offsetof(struct lldb_copy_dyld_cache_header_v1, uuid);
      dsc_uuid = UUID::fromOptionalData(
          dsc_header_data.GetData(&offset, sizeof(uuid_t)), sizeof(uuid_t));
    }
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS));
    if (log && dsc_uuid.IsValid()) {
      log->Printf("Shared cache %s has UUID %s", dyld_shared_cache.GetPath().c_str(), 
                  dsc_uuid.GetAsString().c_str());
    }
    return dsc_uuid;
  }
  
  static SymbolType GetSymbolType(
      const char *&symbol_name, const char *&symbol_name_non_abi_mangled,
      bool &demangled_is_synthesized, const SectionSP &text_section_sp,
      const SectionSP &data_section_sp, const SectionSP &data_dirty_section_sp,
      const SectionSP &data_const_section_sp, const SectionSP &objc_section_sp,
      const SectionSP &symbol_section) {
    SymbolType type = eSymbolTypeInvalid;
  
    const char *symbol_sect_name = symbol_section->GetName().AsCString();
    if (symbol_section->IsDescendant(text_section_sp.get())) {
      if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS |
                                  S_ATTR_SELF_MODIFYING_CODE |
                                  S_ATTR_SOME_INSTRUCTIONS))
        type = eSymbolTypeData;
      else
        type = eSymbolTypeCode;
    } else if (symbol_section->IsDescendant(data_section_sp.get()) ||
               symbol_section->IsDescendant(data_dirty_section_sp.get()) ||
               symbol_section->IsDescendant(data_const_section_sp.get())) {
      if (symbol_sect_name &&
          ::strstr(symbol_sect_name, "__objc") == symbol_sect_name) {
        type = eSymbolTypeRuntime;
  
        if (symbol_name) {
          llvm::StringRef symbol_name_ref(symbol_name);
          if (symbol_name_ref.startswith("_OBJC_")) {
            static const llvm::StringRef g_objc_v2_prefix_class("_OBJC_CLASS_$_");
            static const llvm::StringRef g_objc_v2_prefix_metaclass(
                "_OBJC_METACLASS_$_");
            static const llvm::StringRef g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
            if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) {
              symbol_name_non_abi_mangled = symbol_name + 1;
              symbol_name = symbol_name + g_objc_v2_prefix_class.size();
              type = eSymbolTypeObjCClass;
              demangled_is_synthesized = true;
            } else if (symbol_name_ref.startswith(g_objc_v2_prefix_metaclass)) {
              symbol_name_non_abi_mangled = symbol_name + 1;
              symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
              type = eSymbolTypeObjCMetaClass;
              demangled_is_synthesized = true;
            } else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) {
              symbol_name_non_abi_mangled = symbol_name + 1;
              symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
              type = eSymbolTypeObjCIVar;
              demangled_is_synthesized = true;
            }
          }
        }
      } else if (symbol_sect_name &&
                 ::strstr(symbol_sect_name, "__gcc_except_tab") ==
                     symbol_sect_name) {
        type = eSymbolTypeException;
      } else {
        type = eSymbolTypeData;
      }
    } else if (symbol_sect_name &&
               ::strstr(symbol_sect_name, "__IMPORT") == symbol_sect_name) {
      type = eSymbolTypeTrampoline;
    } else if (symbol_section->IsDescendant(objc_section_sp.get())) {
      type = eSymbolTypeRuntime;
      if (symbol_name && symbol_name[0] == '.') {
        llvm::StringRef symbol_name_ref(symbol_name);
        static const llvm::StringRef g_objc_v1_prefix_class(".objc_class_name_");
        if (symbol_name_ref.startswith(g_objc_v1_prefix_class)) {
          symbol_name_non_abi_mangled = symbol_name;
          symbol_name = symbol_name + g_objc_v1_prefix_class.size();
          type = eSymbolTypeObjCClass;
          demangled_is_synthesized = true;
        }
      }
    }
    return type;
  }
  
  size_t ObjectFileMachO::ParseSymtab() {
    static Timer::Category func_cat(LLVM_PRETTY_FUNCTION);
    Timer scoped_timer(func_cat, "ObjectFileMachO::ParseSymtab () module = %s",
                       m_file.GetFilename().AsCString(""));
    ModuleSP module_sp(GetModule());
    if (!module_sp)
      return 0;
  
    struct symtab_command symtab_load_command = {0, 0, 0, 0, 0, 0};
    struct linkedit_data_command function_starts_load_command = {0, 0, 0, 0};
    struct dyld_info_command dyld_info = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
    typedef AddressDataArray<lldb::addr_t, bool, 100> FunctionStarts;
    FunctionStarts function_starts;
    lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
    uint32_t i;
    FileSpecList dylib_files;
    Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SYMBOLS));
    static const llvm::StringRef g_objc_v2_prefix_class("_OBJC_CLASS_$_");
    static const llvm::StringRef g_objc_v2_prefix_metaclass("_OBJC_METACLASS_$_");
    static const llvm::StringRef g_objc_v2_prefix_ivar("_OBJC_IVAR_$_");
  
    for (i = 0; i < m_header.ncmds; ++i) {
      const lldb::offset_t cmd_offset = offset;
      // Read in the load command and load command size
      struct load_command lc;
      if (m_data.GetU32(&offset, &lc, 2) == NULL)
        break;
      // Watch for the symbol table load command
      switch (lc.cmd) {
      case LC_SYMTAB:
        symtab_load_command.cmd = lc.cmd;
        symtab_load_command.cmdsize = lc.cmdsize;
        // Read in the rest of the symtab load command
        if (m_data.GetU32(&offset, &symtab_load_command.symoff, 4) ==
            0) // fill in symoff, nsyms, stroff, strsize fields
          return 0;
        if (symtab_load_command.symoff == 0) {
          if (log)
            module_sp->LogMessage(log, "LC_SYMTAB.symoff == 0");
          return 0;
        }
  
        if (symtab_load_command.stroff == 0) {
          if (log)
            module_sp->LogMessage(log, "LC_SYMTAB.stroff == 0");
          return 0;
        }
  
        if (symtab_load_command.nsyms == 0) {
          if (log)
            module_sp->LogMessage(log, "LC_SYMTAB.nsyms == 0");
          return 0;
        }
  
        if (symtab_load_command.strsize == 0) {
          if (log)
            module_sp->LogMessage(log, "LC_SYMTAB.strsize == 0");
          return 0;
        }
        break;
  
      case LC_DYLD_INFO:
      case LC_DYLD_INFO_ONLY:
        if (m_data.GetU32(&offset, &dyld_info.rebase_off, 10)) {
          dyld_info.cmd = lc.cmd;
          dyld_info.cmdsize = lc.cmdsize;
        } else {
          memset(&dyld_info, 0, sizeof(dyld_info));
        }
        break;
  
      case LC_LOAD_DYLIB:
      case LC_LOAD_WEAK_DYLIB:
      case LC_REEXPORT_DYLIB:
      case LC_LOADFVMLIB:
      case LC_LOAD_UPWARD_DYLIB: {
        uint32_t name_offset = cmd_offset + m_data.GetU32(&offset);
        const char *path = m_data.PeekCStr(name_offset);
        if (path) {
          FileSpec file_spec(path);
          // Strip the path if there is @rpath, @executable, etc so we just use
          // the basename
          if (path[0] == '@')
            file_spec.GetDirectory().Clear();
  
          if (lc.cmd == LC_REEXPORT_DYLIB) {
            m_reexported_dylibs.AppendIfUnique(file_spec);
          }
  
          dylib_files.Append(file_spec);
        }
      } break;
  
      case LC_FUNCTION_STARTS:
        function_starts_load_command.cmd = lc.cmd;
        function_starts_load_command.cmdsize = lc.cmdsize;
        if (m_data.GetU32(&offset, &function_starts_load_command.dataoff, 2) ==
            NULL) // fill in symoff, nsyms, stroff, strsize fields
          memset(&function_starts_load_command, 0,
                 sizeof(function_starts_load_command));
        break;
  
      default:
        break;
      }
      offset = cmd_offset + lc.cmdsize;
    }
  
    if (symtab_load_command.cmd) {
      Symtab *symtab = m_symtab_up.get();
      SectionList *section_list = GetSectionList();
      if (section_list == NULL)
        return 0;
  
      const uint32_t addr_byte_size = m_data.GetAddressByteSize();
      const ByteOrder byte_order = m_data.GetByteOrder();
      bool bit_width_32 = addr_byte_size == 4;
      const size_t nlist_byte_size =
          bit_width_32 ? sizeof(struct nlist) : sizeof(struct nlist_64);
  
      DataExtractor nlist_data(NULL, 0, byte_order, addr_byte_size);
      DataExtractor strtab_data(NULL, 0, byte_order, addr_byte_size);
      DataExtractor function_starts_data(NULL, 0, byte_order, addr_byte_size);
      DataExtractor indirect_symbol_index_data(NULL, 0, byte_order,
                                               addr_byte_size);
      DataExtractor dyld_trie_data(NULL, 0, byte_order, addr_byte_size);
  
      const addr_t nlist_data_byte_size =
          symtab_load_command.nsyms * nlist_byte_size;
      const addr_t strtab_data_byte_size = symtab_load_command.strsize;
      addr_t strtab_addr = LLDB_INVALID_ADDRESS;
  
      ProcessSP process_sp(m_process_wp.lock());
      Process *process = process_sp.get();
  
      uint32_t memory_module_load_level = eMemoryModuleLoadLevelComplete;
  
      if (process && m_header.filetype != llvm::MachO::MH_OBJECT) {
        Target &target = process->GetTarget();
  
        memory_module_load_level = target.GetMemoryModuleLoadLevel();
  
        SectionSP linkedit_section_sp(
            section_list->FindSectionByName(GetSegmentNameLINKEDIT()));
        // Reading mach file from memory in a process or core file...
  
        if (linkedit_section_sp) {
          addr_t linkedit_load_addr =
              linkedit_section_sp->GetLoadBaseAddress(&target);
          if (linkedit_load_addr == LLDB_INVALID_ADDRESS) {
            // We might be trying to access the symbol table before the
            // __LINKEDIT's load address has been set in the target. We can't
            // fail to read the symbol table, so calculate the right address
            // manually
            linkedit_load_addr = CalculateSectionLoadAddressForMemoryImage(
                m_memory_addr, GetMachHeaderSection(), linkedit_section_sp.get());
          }
  
          const addr_t linkedit_file_offset =
              linkedit_section_sp->GetFileOffset();
          const addr_t symoff_addr = linkedit_load_addr +
                                     symtab_load_command.symoff -
                                     linkedit_file_offset;
          strtab_addr = linkedit_load_addr + symtab_load_command.stroff -
                        linkedit_file_offset;
  
          bool data_was_read = false;
  
  #if defined(__APPLE__) &&                                                      \
      (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
          if (m_header.flags & 0x80000000u &&
              process->GetAddressByteSize() == sizeof(void *)) {
            // This mach-o memory file is in the dyld shared cache. If this
            // program is not remote and this is iOS, then this process will
            // share the same shared cache as the process we are debugging and we
            // can read the entire __LINKEDIT from the address space in this
            // process. This is a needed optimization that is used for local iOS
            // debugging only since all shared libraries in the shared cache do
            // not have corresponding files that exist in the file system of the
            // device. They have been combined into a single file. This means we
            // always have to load these files from memory. All of the symbol and
            // string tables from all of the __LINKEDIT sections from the shared
            // libraries in the shared cache have been merged into a single large
            // symbol and string table. Reading all of this symbol and string
            // table data across can slow down debug launch times, so we optimize
            // this by reading the memory for the __LINKEDIT section from this
            // process.
  
            UUID lldb_shared_cache;
            addr_t lldb_shared_cache_addr;
            GetLLDBSharedCacheUUID (lldb_shared_cache_addr, lldb_shared_cache);
            UUID process_shared_cache;
            addr_t process_shared_cache_addr;
            GetProcessSharedCacheUUID(process, process_shared_cache_addr, process_shared_cache);
            bool use_lldb_cache = true;
            if (lldb_shared_cache.IsValid() && process_shared_cache.IsValid() &&
                (lldb_shared_cache != process_shared_cache
                 || process_shared_cache_addr != lldb_shared_cache_addr)) {
              use_lldb_cache = false;
            }
  
            PlatformSP platform_sp(target.GetPlatform());
            if (platform_sp && platform_sp->IsHost() && use_lldb_cache) {
              data_was_read = true;
              nlist_data.SetData((void *)symoff_addr, nlist_data_byte_size,
                                 eByteOrderLittle);
              strtab_data.SetData((void *)strtab_addr, strtab_data_byte_size,
                                  eByteOrderLittle);
              if (function_starts_load_command.cmd) {
                const addr_t func_start_addr =
                    linkedit_load_addr + function_starts_load_command.dataoff -
                    linkedit_file_offset;
                function_starts_data.SetData(
                    (void *)func_start_addr,
                    function_starts_load_command.datasize, eByteOrderLittle);
              }
            }
          }
  #endif
  
          if (!data_was_read) {
            // Always load dyld - the dynamic linker - from memory if we didn't
            // find a binary anywhere else. lldb will not register
            // dylib/framework/bundle loads/unloads if we don't have the dyld
            // symbols, we force dyld to load from memory despite the user's
            // target.memory-module-load-level setting.
            if (memory_module_load_level == eMemoryModuleLoadLevelComplete ||
                m_header.filetype == llvm::MachO::MH_DYLINKER) {
              DataBufferSP nlist_data_sp(
                  ReadMemory(process_sp, symoff_addr, nlist_data_byte_size));
              if (nlist_data_sp)
                nlist_data.SetData(nlist_data_sp, 0,
                                   nlist_data_sp->GetByteSize());
              if (m_dysymtab.nindirectsyms != 0) {
                const addr_t indirect_syms_addr = linkedit_load_addr +
                                                  m_dysymtab.indirectsymoff -
                                                  linkedit_file_offset;
                DataBufferSP indirect_syms_data_sp(
                    ReadMemory(process_sp, indirect_syms_addr,
                               m_dysymtab.nindirectsyms * 4));
                if (indirect_syms_data_sp)
                  indirect_symbol_index_data.SetData(
                      indirect_syms_data_sp, 0,
                      indirect_syms_data_sp->GetByteSize());
                // If this binary is outside the shared cache, 
                // cache the string table.
                // Binaries in the shared cache all share a giant string table, and
                // we can't share the string tables across multiple ObjectFileMachO's,
                // so we'd end up re-reading this mega-strtab for every binary
                // in the shared cache - it would be a big perf problem.
                // For binaries outside the shared cache, it's faster to read the
                // entire strtab at once instead of piece-by-piece as we process
                // the nlist records.
                if ((m_header.flags & 0x80000000u) == 0) {
                  DataBufferSP strtab_data_sp (ReadMemory (process_sp, strtab_addr, 
                        strtab_data_byte_size));
                  if (strtab_data_sp) {
                    strtab_data.SetData (strtab_data_sp, 0, strtab_data_sp->GetByteSize());
                  }
                }
              }
            }
            if (memory_module_load_level >=
                       eMemoryModuleLoadLevelPartial) {
              if (function_starts_load_command.cmd) {
                const addr_t func_start_addr =
                    linkedit_load_addr + function_starts_load_command.dataoff -
                    linkedit_file_offset;
                DataBufferSP func_start_data_sp(
                    ReadMemory(process_sp, func_start_addr,
                               function_starts_load_command.datasize));
                if (func_start_data_sp)
                  function_starts_data.SetData(func_start_data_sp, 0,
                                               func_start_data_sp->GetByteSize());
              }
            }
          }
        }
      } else {
        nlist_data.SetData(m_data, symtab_load_command.symoff,
                           nlist_data_byte_size);
        strtab_data.SetData(m_data, symtab_load_command.stroff,
                            strtab_data_byte_size);
  
        if (dyld_info.export_size > 0) {
          dyld_trie_data.SetData(m_data, dyld_info.export_off,
                                 dyld_info.export_size);
        }
  
        if (m_dysymtab.nindirectsyms != 0) {
          indirect_symbol_index_data.SetData(m_data, m_dysymtab.indirectsymoff,
                                             m_dysymtab.nindirectsyms * 4);
        }
        if (function_starts_load_command.cmd) {
          function_starts_data.SetData(m_data,
                                       function_starts_load_command.dataoff,
                                       function_starts_load_command.datasize);
        }
      }
  
      if (nlist_data.GetByteSize() == 0 &&
          memory_module_load_level == eMemoryModuleLoadLevelComplete) {
        if (log)
          module_sp->LogMessage(log, "failed to read nlist data");
        return 0;
      }
  
      const bool have_strtab_data = strtab_data.GetByteSize() > 0;
      if (!have_strtab_data) {
        if (process) {
          if (strtab_addr == LLDB_INVALID_ADDRESS) {
            if (log)
              module_sp->LogMessage(log, "failed to locate the strtab in memory");
            return 0;
          }
        } else {
          if (log)
            module_sp->LogMessage(log, "failed to read strtab data");
          return 0;
        }
      }
  
      ConstString g_segment_name_TEXT = GetSegmentNameTEXT();
      ConstString g_segment_name_DATA = GetSegmentNameDATA();
      ConstString g_segment_name_DATA_DIRTY = GetSegmentNameDATA_DIRTY();
      ConstString g_segment_name_DATA_CONST = GetSegmentNameDATA_CONST();
      ConstString g_segment_name_OBJC = GetSegmentNameOBJC();
      ConstString g_section_name_eh_frame = GetSectionNameEHFrame();
      SectionSP text_section_sp(
          section_list->FindSectionByName(g_segment_name_TEXT));
      SectionSP data_section_sp(
          section_list->FindSectionByName(g_segment_name_DATA));
      SectionSP data_dirty_section_sp(
          section_list->FindSectionByName(g_segment_name_DATA_DIRTY));
      SectionSP data_const_section_sp(
          section_list->FindSectionByName(g_segment_name_DATA_CONST));
      SectionSP objc_section_sp(
          section_list->FindSectionByName(g_segment_name_OBJC));
      SectionSP eh_frame_section_sp;
      if (text_section_sp.get())
        eh_frame_section_sp = text_section_sp->GetChildren().FindSectionByName(
            g_section_name_eh_frame);
      else
        eh_frame_section_sp =
            section_list->FindSectionByName(g_section_name_eh_frame);
  
      const bool is_arm = (m_header.cputype == llvm::MachO::CPU_TYPE_ARM);
  
      // lldb works best if it knows the start address of all functions in a
      // module. Linker symbols or debug info are normally the best source of
      // information for start addr / size but they may be stripped in a released
      // binary. Two additional sources of information exist in Mach-O binaries:
      //    LC_FUNCTION_STARTS - a list of ULEB128 encoded offsets of each
      //    function's start address in the
      //                         binary, relative to the text section.
      //    eh_frame           - the eh_frame FDEs have the start addr & size of
      //    each function
      //  LC_FUNCTION_STARTS is the fastest source to read in, and is present on
      //  all modern binaries.
      //  Binaries built to run on older releases may need to use eh_frame
      //  information.
  
      if (text_section_sp && function_starts_data.GetByteSize()) {
        FunctionStarts::Entry function_start_entry;
        function_start_entry.data = false;
        lldb::offset_t function_start_offset = 0;
        function_start_entry.addr = text_section_sp->GetFileAddress();
        uint64_t delta;
        while ((delta = function_starts_data.GetULEB128(&function_start_offset)) >
               0) {
          // Now append the current entry
          function_start_entry.addr += delta;
          function_starts.Append(function_start_entry);
        }
      } else {
        // If m_type is eTypeDebugInfo, then this is a dSYM - it will have the
        // load command claiming an eh_frame but it doesn't actually have the
        // eh_frame content.  And if we have a dSYM, we don't need to do any of
        // this fill-in-the-missing-symbols works anyway - the debug info should
        // give us all the functions in the module.
        if (text_section_sp.get() && eh_frame_section_sp.get() &&
            m_type != eTypeDebugInfo) {
          DWARFCallFrameInfo eh_frame(*this, eh_frame_section_sp,
                                      DWARFCallFrameInfo::EH);
          DWARFCallFrameInfo::FunctionAddressAndSizeVector functions;
          eh_frame.GetFunctionAddressAndSizeVector(functions);
          addr_t text_base_addr = text_section_sp->GetFileAddress();
          size_t count = functions.GetSize();
          for (size_t i = 0; i < count; ++i) {
            const DWARFCallFrameInfo::FunctionAddressAndSizeVector::Entry *func =
                functions.GetEntryAtIndex(i);
            if (func) {
              FunctionStarts::Entry function_start_entry;
              function_start_entry.addr = func->base - text_base_addr;
              function_starts.Append(function_start_entry);
            }
          }
        }
      }
  
      const size_t function_starts_count = function_starts.GetSize();
  
      // For user process binaries (executables, dylibs, frameworks, bundles), if
      // we don't have LC_FUNCTION_STARTS/eh_frame section in this binary, we're
      // going to assume the binary has been stripped.  Don't allow assembly
      // language instruction emulation because we don't know proper function
      // start boundaries.
      //
      // For all other types of binaries (kernels, stand-alone bare board
      // binaries, kexts), they may not have LC_FUNCTION_STARTS / eh_frame
      // sections - we should not make any assumptions about them based on that.
      if (function_starts_count == 0 && CalculateStrata() == eStrataUser) {
        m_allow_assembly_emulation_unwind_plans = false;
        Log *unwind_or_symbol_log(lldb_private::GetLogIfAnyCategoriesSet(
            LIBLLDB_LOG_SYMBOLS | LIBLLDB_LOG_UNWIND));
  
        if (unwind_or_symbol_log)
          module_sp->LogMessage(
              unwind_or_symbol_log,
              "no LC_FUNCTION_STARTS, will not allow assembly profiled unwinds");
      }
  
      const user_id_t TEXT_eh_frame_sectID =
          eh_frame_section_sp.get() ? eh_frame_section_sp->GetID()
                                    : static_cast<user_id_t>(NO_SECT);
  
      lldb::offset_t nlist_data_offset = 0;
  
      uint32_t N_SO_index = UINT32_MAX;
  
      MachSymtabSectionInfo section_info(section_list);
      std::vector<uint32_t> N_FUN_indexes;
      std::vector<uint32_t> N_NSYM_indexes;
      std::vector<uint32_t> N_INCL_indexes;
      std::vector<uint32_t> N_BRAC_indexes;
      std::vector<uint32_t> N_COMM_indexes;
      typedef std::multimap<uint64_t, uint32_t> ValueToSymbolIndexMap;
      typedef std::map<uint32_t, uint32_t> NListIndexToSymbolIndexMap;
      typedef std::map<const char *, uint32_t> ConstNameToSymbolIndexMap;
      ValueToSymbolIndexMap N_FUN_addr_to_sym_idx;
      ValueToSymbolIndexMap N_STSYM_addr_to_sym_idx;
      ConstNameToSymbolIndexMap N_GSYM_name_to_sym_idx;
      // Any symbols that get merged into another will get an entry in this map
      // so we know
      NListIndexToSymbolIndexMap m_nlist_idx_to_sym_idx;
      uint32_t nlist_idx = 0;
      Symbol *symbol_ptr = NULL;
  
      uint32_t sym_idx = 0;
      Symbol *sym = NULL;
      size_t num_syms = 0;
      std::string memory_symbol_name;
      uint32_t unmapped_local_symbols_found = 0;
  
      std::vector<TrieEntryWithOffset> trie_entries;
      std::set<lldb::addr_t> resolver_addresses;
      std::set<lldb::addr_t> symbol_file_addresses;
  
      if (dyld_trie_data.GetByteSize() > 0) {
        std::vector<llvm::StringRef> nameSlices;
        ParseTrieEntries(dyld_trie_data, 0, is_arm,
                         eLazyBoolNo, // eLazyBoolCalculate,
                         nameSlices, resolver_addresses, trie_entries);
  
        ConstString text_segment_name("__TEXT");
        SectionSP text_segment_sp =
            GetSectionList()->FindSectionByName(text_segment_name);
        if (text_segment_sp) {
          const lldb::addr_t text_segment_file_addr =
              text_segment_sp->GetFileAddress();
          if (text_segment_file_addr != LLDB_INVALID_ADDRESS) {
            for (auto &e : trie_entries)
              e.entry.address += text_segment_file_addr;
          }
        }
      }
  
      typedef std::set<ConstString> IndirectSymbols;
      IndirectSymbols indirect_symbol_names;
  
  #if defined(__APPLE__) &&                                                      \
      (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
  
      // Some recent builds of the dyld_shared_cache (hereafter: DSC) have been
      // optimized by moving LOCAL symbols out of the memory mapped portion of
      // the DSC. The symbol information has all been retained, but it isn't
      // available in the normal nlist data. However, there *are* duplicate
      // entries of *some*
      // LOCAL symbols in the normal nlist data. To handle this situation
      // correctly, we must first attempt
      // to parse any DSC unmapped symbol information. If we find any, we set a
      // flag that tells the normal nlist parser to ignore all LOCAL symbols.
  
      if (m_header.flags & 0x80000000u) {
        // Before we can start mapping the DSC, we need to make certain the
        // target process is actually using the cache we can find.
  
        // Next we need to determine the correct path for the dyld shared cache.
  
        ArchSpec header_arch;
        GetArchitecture(header_arch);
        char dsc_path[PATH_MAX];
        char dsc_path_development[PATH_MAX];
  
        snprintf(
            dsc_path, sizeof(dsc_path), "%s%s%s",
            "/System/Library/Caches/com.apple.dyld/", /* IPHONE_DYLD_SHARED_CACHE_DIR
                                                         */
            "dyld_shared_cache_", /* DYLD_SHARED_CACHE_BASE_NAME */
            header_arch.GetArchitectureName());
  
        snprintf(
            dsc_path_development, sizeof(dsc_path), "%s%s%s%s",
            "/System/Library/Caches/com.apple.dyld/", /* IPHONE_DYLD_SHARED_CACHE_DIR
                                                         */
            "dyld_shared_cache_", /* DYLD_SHARED_CACHE_BASE_NAME */
            header_arch.GetArchitectureName(), ".development");
  
        FileSpec dsc_nondevelopment_filespec(dsc_path, false);
        FileSpec dsc_development_filespec(dsc_path_development, false);
        FileSpec dsc_filespec;
  
        UUID dsc_uuid;
        UUID process_shared_cache_uuid;
        addr_t process_shared_cache_base_addr;
  
        if (process) {
          GetProcessSharedCacheUUID(process, process_shared_cache_base_addr, process_shared_cache_uuid);
        }
  
        // First see if we can find an exact match for the inferior process
        // shared cache UUID in the development or non-development shared caches
        // on disk.
        if (process_shared_cache_uuid.IsValid()) {
          if (FileSystem::Instance().Exists(dsc_development_filespec)) {
            UUID dsc_development_uuid = GetSharedCacheUUID(
                dsc_development_filespec, byte_order, addr_byte_size);
            if (dsc_development_uuid.IsValid() &&
                dsc_development_uuid == process_shared_cache_uuid) {
              dsc_filespec = dsc_development_filespec;
              dsc_uuid = dsc_development_uuid;
            }
          }
          if (!dsc_uuid.IsValid() &&
              FileSystem::Instance().Exists(dsc_nondevelopment_filespec)) {
            UUID dsc_nondevelopment_uuid = GetSharedCacheUUID(
                dsc_nondevelopment_filespec, byte_order, addr_byte_size);
            if (dsc_nondevelopment_uuid.IsValid() &&
                dsc_nondevelopment_uuid == process_shared_cache_uuid) {
              dsc_filespec = dsc_nondevelopment_filespec;
              dsc_uuid = dsc_nondevelopment_uuid;
            }
          }
        }
  
        // Failing a UUID match, prefer the development dyld_shared cache if both
        // are present.
        if (!FileSystem::Instance().Exists(dsc_filespec)) {
          if (FileSystem::Instance().Exists(dsc_development_filespec)) {
            dsc_filespec = dsc_development_filespec;
          } else {
            dsc_filespec = dsc_nondevelopment_filespec;
          }
        }
  
        /* The dyld_cache_header has a pointer to the
           dyld_cache_local_symbols_info structure (localSymbolsOffset).
           The dyld_cache_local_symbols_info structure gives us three things:
             1. The start and count of the nlist records in the dyld_shared_cache
           file
             2. The start and size of the strings for these nlist records
             3. The start and count of dyld_cache_local_symbols_entry entries
  
           There is one dyld_cache_local_symbols_entry per dylib/framework in the
           dyld shared cache.
           The "dylibOffset" field is the Mach-O header of this dylib/framework in
           the dyld shared cache.
           The dyld_cache_local_symbols_entry also lists the start of this
           dylib/framework's nlist records
           and the count of how many nlist records there are for this
           dylib/framework.
        */
  
        // Process the dyld shared cache header to find the unmapped symbols
  
        DataBufferSP dsc_data_sp = MapFileData(
            dsc_filespec, sizeof(struct lldb_copy_dyld_cache_header_v1), 0);
        if (!dsc_uuid.IsValid()) {
          dsc_uuid = GetSharedCacheUUID(dsc_filespec, byte_order, addr_byte_size);
        }
        if (dsc_data_sp) {
          DataExtractor dsc_header_data(dsc_data_sp, byte_order, addr_byte_size);
  
          bool uuid_match = true;
          if (dsc_uuid.IsValid() && process) {
            if (process_shared_cache_uuid.IsValid() &&
                dsc_uuid != process_shared_cache_uuid) {
              // The on-disk dyld_shared_cache file is not the same as the one in
              // this process' memory, don't use it.
              uuid_match = false;
              ModuleSP module_sp(GetModule());
              if (module_sp)
                module_sp->ReportWarning("process shared cache does not match "
                                         "on-disk dyld_shared_cache file, some "
                                         "symbol names will be missing.");
            }
          }
  
          offset = offsetof(struct lldb_copy_dyld_cache_header_v1, mappingOffset);
  
          uint32_t mappingOffset = dsc_header_data.GetU32(&offset);
  
          // If the mappingOffset points to a location inside the header, we've
          // opened an old dyld shared cache, and should not proceed further.
          if (uuid_match &&
              mappingOffset >= sizeof(struct lldb_copy_dyld_cache_header_v1)) {
  
            DataBufferSP dsc_mapping_info_data_sp = MapFileData(
                dsc_filespec, sizeof(struct lldb_copy_dyld_cache_mapping_info),
                mappingOffset);
  
            DataExtractor dsc_mapping_info_data(dsc_mapping_info_data_sp,
                                                byte_order, addr_byte_size);
            offset = 0;
  
            // The File addresses (from the in-memory Mach-O load commands) for
            // the shared libraries in the shared library cache need to be
            // adjusted by an offset to match up with the dylibOffset identifying
            // field in the dyld_cache_local_symbol_entry's.  This offset is
            // recorded in mapping_offset_value.
            const uint64_t mapping_offset_value =
                dsc_mapping_info_data.GetU64(&offset);
  
            offset = offsetof(struct lldb_copy_dyld_cache_header_v1,
                              localSymbolsOffset);
            uint64_t localSymbolsOffset = dsc_header_data.GetU64(&offset);
            uint64_t localSymbolsSize = dsc_header_data.GetU64(&offset);
  
            if (localSymbolsOffset && localSymbolsSize) {
              // Map the local symbols
              DataBufferSP dsc_local_symbols_data_sp =
                  MapFileData(dsc_filespec, localSymbolsSize, localSymbolsOffset);
  
              if (dsc_local_symbols_data_sp) {
                DataExtractor dsc_local_symbols_data(dsc_local_symbols_data_sp,
                                                     byte_order, addr_byte_size);
  
                offset = 0;
  
                typedef std::map<ConstString, uint16_t> UndefinedNameToDescMap;
                typedef std::map<uint32_t, ConstString> SymbolIndexToName;
                UndefinedNameToDescMap undefined_name_to_desc;
                SymbolIndexToName reexport_shlib_needs_fixup;
  
                // Read the local_symbols_infos struct in one shot
                struct lldb_copy_dyld_cache_local_symbols_info local_symbols_info;
                dsc_local_symbols_data.GetU32(&offset,
                                              &local_symbols_info.nlistOffset, 6);
  
                SectionSP text_section_sp(
                    section_list->FindSectionByName(GetSegmentNameTEXT()));
  
                uint32_t header_file_offset =
                    (text_section_sp->GetFileAddress() - mapping_offset_value);
  
                offset = local_symbols_info.entriesOffset;
                for (uint32_t entry_index = 0;
                     entry_index < local_symbols_info.entriesCount;
                     entry_index++) {
                  struct lldb_copy_dyld_cache_local_symbols_entry
                      local_symbols_entry;
                  local_symbols_entry.dylibOffset =
                      dsc_local_symbols_data.GetU32(&offset);
                  local_symbols_entry.nlistStartIndex =
                      dsc_local_symbols_data.GetU32(&offset);
                  local_symbols_entry.nlistCount =
                      dsc_local_symbols_data.GetU32(&offset);
  
                  if (header_file_offset == local_symbols_entry.dylibOffset) {
                    unmapped_local_symbols_found = local_symbols_entry.nlistCount;
  
                    // The normal nlist code cannot correctly size the Symbols
                    // array, we need to allocate it here.
                    sym = symtab->Resize(
                        symtab_load_command.nsyms + m_dysymtab.nindirectsyms +
                        unmapped_local_symbols_found - m_dysymtab.nlocalsym);
                    num_syms = symtab->GetNumSymbols();
  
                    nlist_data_offset =
                        local_symbols_info.nlistOffset +
                        (nlist_byte_size * local_symbols_entry.nlistStartIndex);
                    uint32_t string_table_offset =
                        local_symbols_info.stringsOffset;
  
                    for (uint32_t nlist_index = 0;
                         nlist_index < local_symbols_entry.nlistCount;
                         nlist_index++) {
                      /////////////////////////////
                      {
                        struct nlist_64 nlist;
                        if (!dsc_local_symbols_data.ValidOffsetForDataOfSize(
                                nlist_data_offset, nlist_byte_size))
                          break;
  
                        nlist.n_strx = dsc_local_symbols_data.GetU32_unchecked(
                            &nlist_data_offset);
                        nlist.n_type = dsc_local_symbols_data.GetU8_unchecked(
                            &nlist_data_offset);
                        nlist.n_sect = dsc_local_symbols_data.GetU8_unchecked(
                            &nlist_data_offset);
                        nlist.n_desc = dsc_local_symbols_data.GetU16_unchecked(
                            &nlist_data_offset);
                        nlist.n_value =
                            dsc_local_symbols_data.GetAddress_unchecked(
                                &nlist_data_offset);
  
                        SymbolType type = eSymbolTypeInvalid;
                        const char *symbol_name = dsc_local_symbols_data.PeekCStr(
                            string_table_offset + nlist.n_strx);
  
                        if (symbol_name == NULL) {
                          // No symbol should be NULL, even the symbols with no
                          // string values should have an offset zero which
                          // points to an empty C-string
                          Host::SystemLog(
                              Host::eSystemLogError,
                              "error: DSC unmapped local symbol[%u] has invalid "
                              "string table offset 0x%x in %s, ignoring symbol\n",
                              entry_index, nlist.n_strx,
                              module_sp->GetFileSpec().GetPath().c_str());
                          continue;
                        }
                        if (symbol_name[0] == '\0')
                          symbol_name = NULL;
  
                        const char *symbol_name_non_abi_mangled = NULL;
  
                        SectionSP symbol_section;
                        uint32_t symbol_byte_size = 0;
                        bool add_nlist = true;
                        bool is_debug = ((nlist.n_type & N_STAB) != 0);
                        bool demangled_is_synthesized = false;
                        bool is_gsym = false;
                        bool set_value = true;
  
                        assert(sym_idx < num_syms);
  
                        sym[sym_idx].SetDebug(is_debug);
  
                        if (is_debug) {
                          switch (nlist.n_type) {
                          case N_GSYM:
                            // global symbol: name,,NO_SECT,type,0
                            // Sometimes the N_GSYM value contains the address.
  
                            // FIXME: In the .o files, we have a GSYM and a debug
                            // symbol for all the ObjC data.  They
                            // have the same address, but we want to ensure that
                            // we always find only the real symbol, 'cause we
                            // don't currently correctly attribute the
                            // GSYM one to the ObjCClass/Ivar/MetaClass
                            // symbol type.  This is a temporary hack to make
                            // sure the ObjectiveC symbols get treated correctly.
                            // To do this right, we should coalesce all the GSYM
                            // & global symbols that have the same address.
  
                            is_gsym = true;
                            sym[sym_idx].SetExternal(true);
  
                            if (symbol_name && symbol_name[0] == '_' &&
                                symbol_name[1] == 'O') {
                              llvm::StringRef symbol_name_ref(symbol_name);
                              if (symbol_name_ref.startswith(
                                      g_objc_v2_prefix_class)) {
                                symbol_name_non_abi_mangled = symbol_name + 1;
                                symbol_name =
                                    symbol_name + g_objc_v2_prefix_class.size();
                                type = eSymbolTypeObjCClass;
                                demangled_is_synthesized = true;
  
                              } else if (symbol_name_ref.startswith(
                                             g_objc_v2_prefix_metaclass)) {
                                symbol_name_non_abi_mangled = symbol_name + 1;
                                symbol_name = symbol_name +
                                              g_objc_v2_prefix_metaclass.size();
                                type = eSymbolTypeObjCMetaClass;
                                demangled_is_synthesized = true;
                              } else if (symbol_name_ref.startswith(
                                             g_objc_v2_prefix_ivar)) {
                                symbol_name_non_abi_mangled = symbol_name + 1;
                                symbol_name =
                                    symbol_name + g_objc_v2_prefix_ivar.size();
                                type = eSymbolTypeObjCIVar;
                                demangled_is_synthesized = true;
                              }
                            } else {
                              if (nlist.n_value != 0)
                                symbol_section = section_info.GetSection(
                                    nlist.n_sect, nlist.n_value);
                              type = eSymbolTypeData;
                            }
                            break;
  
                          case N_FNAME:
                            // procedure name (f77 kludge): name,,NO_SECT,0,0
                            type = eSymbolTypeCompiler;
                            break;
  
                          case N_FUN:
                            // procedure: name,,n_sect,linenumber,address
                            if (symbol_name) {
                              type = eSymbolTypeCode;
                              symbol_section = section_info.GetSection(
                                  nlist.n_sect, nlist.n_value);
  
                              N_FUN_addr_to_sym_idx.insert(
                                  std::make_pair(nlist.n_value, sym_idx));
                              // We use the current number of symbols in the
                              // symbol table in lieu of using nlist_idx in case
                              // we ever start trimming entries out
                              N_FUN_indexes.push_back(sym_idx);
                            } else {
                              type = eSymbolTypeCompiler;
  
                              if (!N_FUN_indexes.empty()) {
                                // Copy the size of the function into the
                                // original
                                // STAB entry so we don't have
                                // to hunt for it later
                                symtab->SymbolAtIndex(N_FUN_indexes.back())
                                    ->SetByteSize(nlist.n_value);
                                N_FUN_indexes.pop_back();
                                // We don't really need the end function STAB as
                                // it contains the size which we already placed
                                // with the original symbol, so don't add it if
                                // we want a minimal symbol table
                                add_nlist = false;
                              }
                            }
                            break;
  
                          case N_STSYM:
                            // static symbol: name,,n_sect,type,address
                            N_STSYM_addr_to_sym_idx.insert(
                                std::make_pair(nlist.n_value, sym_idx));
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
                            if (symbol_name && symbol_name[0]) {
                              type = ObjectFile::GetSymbolTypeFromName(
                                  symbol_name + 1, eSymbolTypeData);
                            }
                            break;
  
                          case N_LCSYM:
                            // .lcomm symbol: name,,n_sect,type,address
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
                            type = eSymbolTypeCommonBlock;
                            break;
  
                          case N_BNSYM:
                            // We use the current number of symbols in the symbol
                            // table in lieu of using nlist_idx in case we ever
                            // start trimming entries out Skip these if we want
                            // minimal symbol tables
                            add_nlist = false;
                            break;
  
                          case N_ENSYM:
                            // Set the size of the N_BNSYM to the terminating
                            // index of this N_ENSYM so that we can always skip
                            // the entire symbol if we need to navigate more
                            // quickly at the source level when parsing STABS
                            // Skip these if we want minimal symbol tables
                            add_nlist = false;
                            break;
  
                          case N_OPT:
                            // emitted with gcc2_compiled and in gcc source
                            type = eSymbolTypeCompiler;
                            break;
  
                          case N_RSYM:
                            // register sym: name,,NO_SECT,type,register
                            type = eSymbolTypeVariable;
                            break;
  
                          case N_SLINE:
                            // src line: 0,,n_sect,linenumber,address
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
                            type = eSymbolTypeLineEntry;
                            break;
  
                          case N_SSYM:
                            // structure elt: name,,NO_SECT,type,struct_offset
                            type = eSymbolTypeVariableType;
                            break;
  
                          case N_SO:
                            // source file name
                            type = eSymbolTypeSourceFile;
                            if (symbol_name == NULL) {
                              add_nlist = false;
                              if (N_SO_index != UINT32_MAX) {
                                // Set the size of the N_SO to the terminating
                                // index of this N_SO so that we can always skip
                                // the entire N_SO if we need to navigate more
                                // quickly at the source level when parsing STABS
                                symbol_ptr = symtab->SymbolAtIndex(N_SO_index);
                                symbol_ptr->SetByteSize(sym_idx);
                                symbol_ptr->SetSizeIsSibling(true);
                              }
                              N_NSYM_indexes.clear();
                              N_INCL_indexes.clear();
                              N_BRAC_indexes.clear();
                              N_COMM_indexes.clear();
                              N_FUN_indexes.clear();
                              N_SO_index = UINT32_MAX;
                            } else {
                              // We use the current number of symbols in the
                              // symbol table in lieu of using nlist_idx in case
                              // we ever start trimming entries out
                              const bool N_SO_has_full_path =
                                  symbol_name[0] == '/';
                              if (N_SO_has_full_path) {
                                if ((N_SO_index == sym_idx - 1) &&
                                    ((sym_idx - 1) < num_syms)) {
                                  // We have two consecutive N_SO entries where
                                  // the first contains a directory and the
                                  // second contains a full path.
                                  sym[sym_idx - 1].GetMangled().SetValue(
                                      ConstString(symbol_name), false);
                                  m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
                                  add_nlist = false;
                                } else {
                                  // This is the first entry in a N_SO that
                                  // contains a directory or
                                  // a full path to the source file
                                  N_SO_index = sym_idx;
                                }
                              } else if ((N_SO_index == sym_idx - 1) &&
                                         ((sym_idx - 1) < num_syms)) {
                                // This is usually the second N_SO entry that
                                // contains just the filename, so here we combine
                                // it with the first one if we are minimizing the
                                // symbol table
                                const char *so_path =
                                    sym[sym_idx - 1]
                                        .GetMangled()
                                        .GetDemangledName(
                                            lldb::eLanguageTypeUnknown)
                                        .AsCString();
                                if (so_path && so_path[0]) {
                                  std::string full_so_path(so_path);
                                  const size_t double_slash_pos =
                                      full_so_path.find("//");
                                  if (double_slash_pos != std::string::npos) {
                                    // The linker has been generating bad N_SO
                                    // entries with doubled up paths
                                    // in the format "%s%s" where the first
                                    // string in the DW_AT_comp_dir, and the
                                    // second is the directory for the source
                                    // file so you end up with a path that looks
                                    // like "/tmp/src//tmp/src/"
                                    FileSpec so_dir(so_path, false);
                                    if (!FileSystem::Instance().Exists(so_dir)) {
                                      so_dir.SetFile(
                                          &full_so_path[double_slash_pos + 1],
                                          false);
                                      if (FileSystem::Instance().Exists(so_dir)) {
                                        // Trim off the incorrect path
                                        full_so_path.erase(0,
                                                           double_slash_pos + 1);
                                      }
                                    }
                                  }
                                  if (*full_so_path.rbegin() != '/')
                                    full_so_path += '/';
                                  full_so_path += symbol_name;
                                  sym[sym_idx - 1].GetMangled().SetValue(
                                      ConstString(full_so_path.c_str()), false);
                                  add_nlist = false;
                                  m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
                                }
                              } else {
                                // This could be a relative path to a N_SO
                                N_SO_index = sym_idx;
                              }
                            }
                            break;
  
                          case N_OSO:
                            // object file name: name,,0,0,st_mtime
                            type = eSymbolTypeObjectFile;
                            break;
  
                          case N_LSYM:
                            // local sym: name,,NO_SECT,type,offset
                            type = eSymbolTypeLocal;
                            break;
  
                          //----------------------------------------------------------------------
                          // INCL scopes
                          //----------------------------------------------------------------------
                          case N_BINCL:
                            // include file beginning: name,,NO_SECT,0,sum We use
                            // the current number of symbols in the symbol table
                            // in lieu of using nlist_idx in case we ever start
                            // trimming entries out
                            N_INCL_indexes.push_back(sym_idx);
                            type = eSymbolTypeScopeBegin;
                            break;
  
                          case N_EINCL:
                            // include file end: name,,NO_SECT,0,0
                            // Set the size of the N_BINCL to the terminating
                            // index of this N_EINCL so that we can always skip
                            // the entire symbol if we need to navigate more
                            // quickly at the source level when parsing STABS
                            if (!N_INCL_indexes.empty()) {
                              symbol_ptr =
                                  symtab->SymbolAtIndex(N_INCL_indexes.back());
                              symbol_ptr->SetByteSize(sym_idx + 1);
                              symbol_ptr->SetSizeIsSibling(true);
                              N_INCL_indexes.pop_back();
                            }
                            type = eSymbolTypeScopeEnd;
                            break;
  
                          case N_SOL:
                            // #included file name: name,,n_sect,0,address
                            type = eSymbolTypeHeaderFile;
  
                            // We currently don't use the header files on darwin
                            add_nlist = false;
                            break;
  
                          case N_PARAMS:
                            // compiler parameters: name,,NO_SECT,0,0
                            type = eSymbolTypeCompiler;
                            break;
  
                          case N_VERSION:
                            // compiler version: name,,NO_SECT,0,0
                            type = eSymbolTypeCompiler;
                            break;
  
                          case N_OLEVEL:
                            // compiler -O level: name,,NO_SECT,0,0
                            type = eSymbolTypeCompiler;
                            break;
  
                          case N_PSYM:
                            // parameter: name,,NO_SECT,type,offset
                            type = eSymbolTypeVariable;
                            break;
  
                          case N_ENTRY:
                            // alternate entry: name,,n_sect,linenumber,address
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
                            type = eSymbolTypeLineEntry;
                            break;
  
                          //----------------------------------------------------------------------
                          // Left and Right Braces
                          //----------------------------------------------------------------------
                          case N_LBRAC:
                            // left bracket: 0,,NO_SECT,nesting level,address We
                            // use the current number of symbols in the symbol
                            // table in lieu of using nlist_idx in case we ever
                            // start trimming entries out
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
                            N_BRAC_indexes.push_back(sym_idx);
                            type = eSymbolTypeScopeBegin;
                            break;
  
                          case N_RBRAC:
                            // right bracket: 0,,NO_SECT,nesting level,address
                            // Set the size of the N_LBRAC to the terminating
                            // index of this N_RBRAC so that we can always skip
                            // the entire symbol if we need to navigate more
                            // quickly at the source level when parsing STABS
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
                            if (!N_BRAC_indexes.empty()) {
                              symbol_ptr =
                                  symtab->SymbolAtIndex(N_BRAC_indexes.back());
                              symbol_ptr->SetByteSize(sym_idx + 1);
                              symbol_ptr->SetSizeIsSibling(true);
                              N_BRAC_indexes.pop_back();
                            }
                            type = eSymbolTypeScopeEnd;
                            break;
  
                          case N_EXCL:
                            // deleted include file: name,,NO_SECT,0,sum
                            type = eSymbolTypeHeaderFile;
                            break;
  
                          //----------------------------------------------------------------------
                          // COMM scopes
                          //----------------------------------------------------------------------
                          case N_BCOMM:
                            // begin common: name,,NO_SECT,0,0
                            // We use the current number of symbols in the symbol
                            // table in lieu of using nlist_idx in case we ever
                            // start trimming entries out
                            type = eSymbolTypeScopeBegin;
                            N_COMM_indexes.push_back(sym_idx);
                            break;
  
                          case N_ECOML:
                            // end common (local name): 0,,n_sect,0,address
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
                          // Fall through
  
                          case N_ECOMM:
                            // end common: name,,n_sect,0,0
                            // Set the size of the N_BCOMM to the terminating
                            // index of this N_ECOMM/N_ECOML so that we can
                            // always skip the entire symbol if we need to
                            // navigate more quickly at the source level when
                            // parsing STABS
                            if (!N_COMM_indexes.empty()) {
                              symbol_ptr =
                                  symtab->SymbolAtIndex(N_COMM_indexes.back());
                              symbol_ptr->SetByteSize(sym_idx + 1);
                              symbol_ptr->SetSizeIsSibling(true);
                              N_COMM_indexes.pop_back();
                            }
                            type = eSymbolTypeScopeEnd;
                            break;
  
                          case N_LENG:
                            // second stab entry with length information
                            type = eSymbolTypeAdditional;
                            break;
  
                          default:
                            break;
                          }
                        } else {
                          // uint8_t n_pext    = N_PEXT & nlist.n_type;
                          uint8_t n_type = N_TYPE & nlist.n_type;
                          sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0);
  
                          switch (n_type) {
                          case N_INDR: {
                            const char *reexport_name_cstr =
                                strtab_data.PeekCStr(nlist.n_value);
                            if (reexport_name_cstr && reexport_name_cstr[0]) {
                              type = eSymbolTypeReExported;
                              ConstString reexport_name(
                                  reexport_name_cstr +
                                  ((reexport_name_cstr[0] == '_') ? 1 : 0));
                              sym[sym_idx].SetReExportedSymbolName(reexport_name);
                              set_value = false;
                              reexport_shlib_needs_fixup[sym_idx] = reexport_name;
                              indirect_symbol_names.insert(
                                  ConstString(symbol_name +
                                              ((symbol_name[0] == '_') ? 1 : 0)));
                            } else
                              type = eSymbolTypeUndefined;
                          } break;
  
                          case N_UNDF:
                            if (symbol_name && symbol_name[0]) {
                              ConstString undefined_name(
                                  symbol_name +
                                  ((symbol_name[0] == '_') ? 1 : 0));
                              undefined_name_to_desc[undefined_name] =
                                  nlist.n_desc;
                            }
                          // Fall through
                          case N_PBUD:
                            type = eSymbolTypeUndefined;
                            break;
  
                          case N_ABS:
                            type = eSymbolTypeAbsolute;
                            break;
  
                          case N_SECT: {
                            symbol_section = section_info.GetSection(
                                nlist.n_sect, nlist.n_value);
  
                            if (symbol_section == NULL) {
                              // TODO: warn about this?
                              add_nlist = false;
                              break;
                            }
  
                            if (TEXT_eh_frame_sectID == nlist.n_sect) {
                              type = eSymbolTypeException;
                            } else {
                              uint32_t section_type =
                                  symbol_section->Get() & SECTION_TYPE;
  
                              switch (section_type) {
                              case S_CSTRING_LITERALS:
                                type = eSymbolTypeData;
                                break; // section with only literal C strings
                              case S_4BYTE_LITERALS:
                                type = eSymbolTypeData;
                                break; // section with only 4 byte literals
                              case S_8BYTE_LITERALS:
                                type = eSymbolTypeData;
                                break; // section with only 8 byte literals
                              case S_LITERAL_POINTERS:
                                type = eSymbolTypeTrampoline;
                                break; // section with only pointers to literals
                              case S_NON_LAZY_SYMBOL_POINTERS:
                                type = eSymbolTypeTrampoline;
                                break; // section with only non-lazy symbol
                                       // pointers
                              case S_LAZY_SYMBOL_POINTERS:
                                type = eSymbolTypeTrampoline;
                                break; // section with only lazy symbol pointers
                              case S_SYMBOL_STUBS:
                                type = eSymbolTypeTrampoline;
                                break; // section with only symbol stubs, byte
                                       // size of stub in the reserved2 field
                              case S_MOD_INIT_FUNC_POINTERS:
                                type = eSymbolTypeCode;
                                break; // section with only function pointers for
                                       // initialization
                              case S_MOD_TERM_FUNC_POINTERS:
                                type = eSymbolTypeCode;
                                break; // section with only function pointers for
                                       // termination
                              case S_INTERPOSING:
                                type = eSymbolTypeTrampoline;
                                break; // section with only pairs of function
                                       // pointers for interposing
                              case S_16BYTE_LITERALS:
                                type = eSymbolTypeData;
                                break; // section with only 16 byte literals
                              case S_DTRACE_DOF:
                                type = eSymbolTypeInstrumentation;
                                break;
                              case S_LAZY_DYLIB_SYMBOL_POINTERS:
                                type = eSymbolTypeTrampoline;
                                break;
                              default:
                                switch (symbol_section->GetType()) {
                                case lldb::eSectionTypeCode:
                                  type = eSymbolTypeCode;
                                  break;
                                case eSectionTypeData:
                                case eSectionTypeDataCString: // Inlined C string
                                                              // data
                                case eSectionTypeDataCStringPointers: // Pointers
                                                                      // to C
                                                                      // string
                                                                      // data
                                case eSectionTypeDataSymbolAddress: // Address of
                                                                    // a symbol in
                                                                    // the symbol
                                                                    // table
                                case eSectionTypeData4:
                                case eSectionTypeData8:
                                case eSectionTypeData16:
                                  type = eSymbolTypeData;
                                  break;
                                default:
                                  break;
                                }
                                break;
                              }
  
                              if (type == eSymbolTypeInvalid) {
                                const char *symbol_sect_name =
                                    symbol_section->GetName().AsCString();
                                if (symbol_section->IsDescendant(
                                        text_section_sp.get())) {
                                  if (symbol_section->IsClear(
                                          S_ATTR_PURE_INSTRUCTIONS |
                                          S_ATTR_SELF_MODIFYING_CODE |
                                          S_ATTR_SOME_INSTRUCTIONS))
                                    type = eSymbolTypeData;
                                  else
                                    type = eSymbolTypeCode;
                                } else if (symbol_section->IsDescendant(
                                               data_section_sp.get()) ||
                                           symbol_section->IsDescendant(
                                               data_dirty_section_sp.get()) ||
                                           symbol_section->IsDescendant(
                                               data_const_section_sp.get())) {
                                  if (symbol_sect_name &&
                                      ::strstr(symbol_sect_name, "__objc") ==
                                          symbol_sect_name) {
                                    type = eSymbolTypeRuntime;
  
                                    if (symbol_name) {
                                      llvm::StringRef symbol_name_ref(
                                          symbol_name);
                                      if (symbol_name_ref.startswith("_OBJC_")) {
                                        static const llvm::StringRef
                                            g_objc_v2_prefix_class(
                                                "_OBJC_CLASS_$_");
                                        static const llvm::StringRef
                                            g_objc_v2_prefix_metaclass(
                                                "_OBJC_METACLASS_$_");
                                        static const llvm::StringRef
                                            g_objc_v2_prefix_ivar(
                                                "_OBJC_IVAR_$_");
                                        if (symbol_name_ref.startswith(
                                                g_objc_v2_prefix_class)) {
                                          symbol_name_non_abi_mangled =
                                              symbol_name + 1;
                                          symbol_name =
                                              symbol_name +
                                              g_objc_v2_prefix_class.size();
                                          type = eSymbolTypeObjCClass;
                                          demangled_is_synthesized = true;
                                        } else if (
                                            symbol_name_ref.startswith(
                                                g_objc_v2_prefix_metaclass)) {
                                          symbol_name_non_abi_mangled =
                                              symbol_name + 1;
                                          symbol_name =
                                              symbol_name +
                                              g_objc_v2_prefix_metaclass.size();
                                          type = eSymbolTypeObjCMetaClass;
                                          demangled_is_synthesized = true;
                                        } else if (symbol_name_ref.startswith(
                                                       g_objc_v2_prefix_ivar)) {
                                          symbol_name_non_abi_mangled =
                                              symbol_name + 1;
                                          symbol_name =
                                              symbol_name +
                                              g_objc_v2_prefix_ivar.size();
                                          type = eSymbolTypeObjCIVar;
                                          demangled_is_synthesized = true;
                                        }
                                      }
                                    }
                                  } else if (symbol_sect_name &&
                                             ::strstr(symbol_sect_name,
                                                      "__gcc_except_tab") ==
                                                 symbol_sect_name) {
                                    type = eSymbolTypeException;
                                  } else {
                                    type = eSymbolTypeData;
                                  }
                                } else if (symbol_sect_name &&
                                           ::strstr(symbol_sect_name,
                                                    "__IMPORT") ==
                                               symbol_sect_name) {
                                  type = eSymbolTypeTrampoline;
                                } else if (symbol_section->IsDescendant(
                                               objc_section_sp.get())) {
                                  type = eSymbolTypeRuntime;
                                  if (symbol_name && symbol_name[0] == '.') {
                                    llvm::StringRef symbol_name_ref(symbol_name);
                                    static const llvm::StringRef
                                        g_objc_v1_prefix_class(
                                            ".objc_class_name_");
                                    if (symbol_name_ref.startswith(
                                            g_objc_v1_prefix_class)) {
                                      symbol_name_non_abi_mangled = symbol_name;
                                      symbol_name = symbol_name +
                                                    g_objc_v1_prefix_class.size();
                                      type = eSymbolTypeObjCClass;
                                      demangled_is_synthesized = true;
                                    }
                                  }
                                }
                              }
                            }
                          } break;
                          }
                        }
  
                        if (add_nlist) {
                          uint64_t symbol_value = nlist.n_value;
                          if (symbol_name_non_abi_mangled) {
                            sym[sym_idx].GetMangled().SetMangledName(
                                ConstString(symbol_name_non_abi_mangled));
                            sym[sym_idx].GetMangled().SetDemangledName(
                                ConstString(symbol_name));
                          } else {
                            bool symbol_name_is_mangled = false;
  
                            if (symbol_name && symbol_name[0] == '_') {
                              symbol_name_is_mangled = symbol_name[1] == '_';
                              symbol_name++; // Skip the leading underscore
                            }
  
                            if (symbol_name) {
                              ConstString const_symbol_name(symbol_name);
                              sym[sym_idx].GetMangled().SetValue(
                                  const_symbol_name, symbol_name_is_mangled);
                              if (is_gsym && is_debug) {
                                const char *gsym_name =
                                    sym[sym_idx]
                                        .GetMangled()
                                        .GetName(lldb::eLanguageTypeUnknown,
                                                 Mangled::ePreferMangled)
                                        .GetCString();
                                if (gsym_name)
                                  N_GSYM_name_to_sym_idx[gsym_name] = sym_idx;
                              }
                            }
                          }
                          if (symbol_section) {
                            // Keep track of the symbols we added by address in
                            // case we have other sources
                            // for symbols where we only want to add a symbol if
                            // it isn't already in the
                            // symbol table.
                            symbol_file_addresses.insert(nlist.n_value);
  
                            const addr_t section_file_addr =
                                symbol_section->GetFileAddress();
                            if (symbol_byte_size == 0 &&
                                function_starts_count > 0) {
                              addr_t symbol_lookup_file_addr = nlist.n_value;
                              // Do an exact address match for non-ARM addresses,
                              // else get the closest since the symbol might be a
                              // thumb symbol which has an address with bit zero
                              // set
                              FunctionStarts::Entry *func_start_entry =
                                  function_starts.FindEntry(
                                      symbol_lookup_file_addr, !is_arm);
                              if (is_arm && func_start_entry) {
                                // Verify that the function start address is the
                                // symbol address (ARM) or the symbol address + 1
                                // (thumb)
                                if (func_start_entry->addr !=
                                        symbol_lookup_file_addr &&
                                    func_start_entry->addr !=
                                        (symbol_lookup_file_addr + 1)) {
                                  // Not the right entry, NULL it out...
                                  func_start_entry = NULL;
                                }
                              }
                              if (func_start_entry) {
                                func_start_entry->data = true;
  
                                addr_t symbol_file_addr = func_start_entry->addr;
                                uint32_t symbol_flags = 0;
                                if (is_arm) {
                                  if (symbol_file_addr & 1)
                                    symbol_flags =
                                        MACHO_NLIST_ARM_SYMBOL_IS_THUMB;
                                  symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
                                }
  
                                const FunctionStarts::Entry
                                    *next_func_start_entry =
                                        function_starts.FindNextEntry(
                                            func_start_entry);
                                const addr_t section_end_file_addr =
                                    section_file_addr +
                                    symbol_section->GetByteSize();
                                if (next_func_start_entry) {
                                  addr_t next_symbol_file_addr =
                                      next_func_start_entry->addr;
                                  // Be sure the clear the Thumb address bit when
                                  // we calculate the size from the current and
                                  // next address
                                  if (is_arm)
                                    next_symbol_file_addr &=
                                        THUMB_ADDRESS_BIT_MASK;
                                  symbol_byte_size = std::min<lldb::addr_t>(
                                      next_symbol_file_addr - symbol_file_addr,
                                      section_end_file_addr - symbol_file_addr);
                                } else {
                                  symbol_byte_size =
                                      section_end_file_addr - symbol_file_addr;
                                }
                              }
                            }
                            symbol_value -= section_file_addr;
                          }
  
                          if (is_debug == false) {
                            if (type == eSymbolTypeCode) {
                              // See if we can find a N_FUN entry for any code
                              // symbols. If we do find a match, and the name
                              // matches, then we can merge the two into just the
                              // function symbol to avoid duplicate entries in
                              // the symbol table
                              std::pair<ValueToSymbolIndexMap::const_iterator,
                                        ValueToSymbolIndexMap::const_iterator>
                                  range;
                              range = N_FUN_addr_to_sym_idx.equal_range(
                                  nlist.n_value);
                              if (range.first != range.second) {
                                bool found_it = false;
                                for (ValueToSymbolIndexMap::const_iterator pos =
                                         range.first;
                                     pos != range.second; ++pos) {
                                  if (sym[sym_idx].GetMangled().GetName(
                                          lldb::eLanguageTypeUnknown,
                                          Mangled::ePreferMangled) ==
                                      sym[pos->second].GetMangled().GetName(
                                          lldb::eLanguageTypeUnknown,
                                          Mangled::ePreferMangled)) {
                                    m_nlist_idx_to_sym_idx[nlist_idx] =
                                        pos->second;
                                    // We just need the flags from the linker
                                    // symbol, so put these flags
                                    // into the N_FUN flags to avoid duplicate
                                    // symbols in the symbol table
                                    sym[pos->second].SetExternal(
                                        sym[sym_idx].IsExternal());
                                    sym[pos->second].SetFlags(nlist.n_type << 16 |
                                                              nlist.n_desc);
                                    if (resolver_addresses.find(nlist.n_value) !=
                                        resolver_addresses.end())
                                      sym[pos->second].SetType(
                                          eSymbolTypeResolver);
                                    sym[sym_idx].Clear();
                                    found_it = true;
                                    break;
                                  }
                                }
                                if (found_it)
                                  continue;
                              } else {
                                if (resolver_addresses.find(nlist.n_value) !=
                                    resolver_addresses.end())
                                  type = eSymbolTypeResolver;
                              }
                            } else if (type == eSymbolTypeData ||
                                       type == eSymbolTypeObjCClass ||
                                       type == eSymbolTypeObjCMetaClass ||
                                       type == eSymbolTypeObjCIVar) {
                              // See if we can find a N_STSYM entry for any data
                              // symbols. If we do find a match, and the name
                              // matches, then we can merge the two into just the
                              // Static symbol to avoid duplicate entries in the
                              // symbol table
                              std::pair<ValueToSymbolIndexMap::const_iterator,
                                        ValueToSymbolIndexMap::const_iterator>
                                  range;
                              range = N_STSYM_addr_to_sym_idx.equal_range(
                                  nlist.n_value);
                              if (range.first != range.second) {
                                bool found_it = false;
                                for (ValueToSymbolIndexMap::const_iterator pos =
                                         range.first;
                                     pos != range.second; ++pos) {
                                  if (sym[sym_idx].GetMangled().GetName(
                                          lldb::eLanguageTypeUnknown,
                                          Mangled::ePreferMangled) ==
                                      sym[pos->second].GetMangled().GetName(
                                          lldb::eLanguageTypeUnknown,
                                          Mangled::ePreferMangled)) {
                                    m_nlist_idx_to_sym_idx[nlist_idx] =
                                        pos->second;
                                    // We just need the flags from the linker
                                    // symbol, so put these flags
                                    // into the N_STSYM flags to avoid duplicate
                                    // symbols in the symbol table
                                    sym[pos->second].SetExternal(
                                        sym[sym_idx].IsExternal());
                                    sym[pos->second].SetFlags(nlist.n_type << 16 |
                                                              nlist.n_desc);
                                    sym[sym_idx].Clear();
                                    found_it = true;
                                    break;
                                  }
                                }
                                if (found_it)
                                  continue;
                              } else {
                                const char *gsym_name =
                                    sym[sym_idx]
                                        .GetMangled()
                                        .GetName(lldb::eLanguageTypeUnknown,
                                                 Mangled::ePreferMangled)
                                        .GetCString();
                                if (gsym_name) {
                                  // Combine N_GSYM stab entries with the non
                                  // stab symbol
                                  ConstNameToSymbolIndexMap::const_iterator pos =
                                      N_GSYM_name_to_sym_idx.find(gsym_name);
                                  if (pos != N_GSYM_name_to_sym_idx.end()) {
                                    const uint32_t GSYM_sym_idx = pos->second;
                                    m_nlist_idx_to_sym_idx[nlist_idx] =
                                        GSYM_sym_idx;
                                    // Copy the address, because often the N_GSYM
                                    // address has an invalid address of zero
                                    // when the global is a common symbol
                                    sym[GSYM_sym_idx].GetAddressRef().SetSection(
                                        symbol_section);
                                    sym[GSYM_sym_idx].GetAddressRef().SetOffset(
                                        symbol_value);
                                    // We just need the flags from the linker
                                    // symbol, so put these flags
                                    // into the N_GSYM flags to avoid duplicate
                                    // symbols in the symbol table
                                    sym[GSYM_sym_idx].SetFlags(
                                        nlist.n_type << 16 | nlist.n_desc);
                                    sym[sym_idx].Clear();
                                    continue;
                                  }
                                }
                              }
                            }
                          }
  
                          sym[sym_idx].SetID(nlist_idx);
                          sym[sym_idx].SetType(type);
                          if (set_value) {
                            sym[sym_idx].GetAddressRef().SetSection(
                                symbol_section);
                            sym[sym_idx].GetAddressRef().SetOffset(symbol_value);
                          }
                          sym[sym_idx].SetFlags(nlist.n_type << 16 |
                                                nlist.n_desc);
  
                          if (symbol_byte_size > 0)
                            sym[sym_idx].SetByteSize(symbol_byte_size);
  
                          if (demangled_is_synthesized)
                            sym[sym_idx].SetDemangledNameIsSynthesized(true);
                          ++sym_idx;
                        } else {
                          sym[sym_idx].Clear();
                        }
                      }
                      /////////////////////////////
                    }
                    break; // No more entries to consider
                  }
                }
  
                for (const auto &pos : reexport_shlib_needs_fixup) {
                  const auto undef_pos = undefined_name_to_desc.find(pos.second);
                  if (undef_pos != undefined_name_to_desc.end()) {
                    const uint8_t dylib_ordinal =
                        llvm::MachO::GET_LIBRARY_ORDINAL(undef_pos->second);
                    if (dylib_ordinal > 0 &&
                        dylib_ordinal < dylib_files.GetSize())
                      sym[pos.first].SetReExportedSymbolSharedLibrary(
                          dylib_files.GetFileSpecAtIndex(dylib_ordinal - 1));
                  }
                }
              }
            }
          }
        }
      }
  
      // Must reset this in case it was mutated above!
      nlist_data_offset = 0;
  #endif
  
      if (nlist_data.GetByteSize() > 0) {
  
        // If the sym array was not created while parsing the DSC unmapped
        // symbols, create it now.
        if (sym == NULL) {
          sym = symtab->Resize(symtab_load_command.nsyms +
                               m_dysymtab.nindirectsyms);
          num_syms = symtab->GetNumSymbols();
        }
  
        if (unmapped_local_symbols_found) {
          assert(m_dysymtab.ilocalsym == 0);
          nlist_data_offset += (m_dysymtab.nlocalsym * nlist_byte_size);
          nlist_idx = m_dysymtab.nlocalsym;
        } else {
          nlist_idx = 0;
        }
  
        typedef std::map<ConstString, uint16_t> UndefinedNameToDescMap;
        typedef std::map<uint32_t, ConstString> SymbolIndexToName;
        UndefinedNameToDescMap undefined_name_to_desc;
        SymbolIndexToName reexport_shlib_needs_fixup;
        for (; nlist_idx < symtab_load_command.nsyms; ++nlist_idx) {
          struct nlist_64 nlist;
          if (!nlist_data.ValidOffsetForDataOfSize(nlist_data_offset,
                                                   nlist_byte_size))
            break;
  
          nlist.n_strx = nlist_data.GetU32_unchecked(&nlist_data_offset);
          nlist.n_type = nlist_data.GetU8_unchecked(&nlist_data_offset);
          nlist.n_sect = nlist_data.GetU8_unchecked(&nlist_data_offset);
          nlist.n_desc = nlist_data.GetU16_unchecked(&nlist_data_offset);
          nlist.n_value = nlist_data.GetAddress_unchecked(&nlist_data_offset);
  
          SymbolType type = eSymbolTypeInvalid;
          const char *symbol_name = NULL;
  
          if (have_strtab_data) {
            symbol_name = strtab_data.PeekCStr(nlist.n_strx);
  
            if (symbol_name == NULL) {
              // No symbol should be NULL, even the symbols with no string values
              // should have an offset zero which points to an empty C-string
              Host::SystemLog(Host::eSystemLogError,
                              "error: symbol[%u] has invalid string table offset "
                              "0x%x in %s, ignoring symbol\n",
                              nlist_idx, nlist.n_strx,
                              module_sp->GetFileSpec().GetPath().c_str());
              continue;
            }
            if (symbol_name[0] == '\0')
              symbol_name = NULL;
          } else {
            const addr_t str_addr = strtab_addr + nlist.n_strx;
            Status str_error;
            if (process->ReadCStringFromMemory(str_addr, memory_symbol_name,
                                               str_error))
              symbol_name = memory_symbol_name.c_str();
          }
          const char *symbol_name_non_abi_mangled = NULL;
  
          SectionSP symbol_section;
          lldb::addr_t symbol_byte_size = 0;
          bool add_nlist = true;
          bool is_gsym = false;
          bool is_debug = ((nlist.n_type & N_STAB) != 0);
          bool demangled_is_synthesized = false;
          bool set_value = true;
          assert(sym_idx < num_syms);
  
          sym[sym_idx].SetDebug(is_debug);
  
          if (is_debug) {
            switch (nlist.n_type) {
            case N_GSYM:
              // global symbol: name,,NO_SECT,type,0
              // Sometimes the N_GSYM value contains the address.
  
              // FIXME: In the .o files, we have a GSYM and a debug symbol for all
              // the ObjC data.  They
              // have the same address, but we want to ensure that we always find
              // only the real symbol, 'cause we don't currently correctly
              // attribute the GSYM one to the ObjCClass/Ivar/MetaClass symbol
              // type.  This is a temporary hack to make sure the ObjectiveC
              // symbols get treated correctly.  To do this right, we should
              // coalesce all the GSYM & global symbols that have the same
              // address.
              is_gsym = true;
              sym[sym_idx].SetExternal(true);
  
              if (symbol_name && symbol_name[0] == '_' && symbol_name[1] == 'O') {
                llvm::StringRef symbol_name_ref(symbol_name);
                if (symbol_name_ref.startswith(g_objc_v2_prefix_class)) {
                  symbol_name_non_abi_mangled = symbol_name + 1;
                  symbol_name = symbol_name + g_objc_v2_prefix_class.size();
                  type = eSymbolTypeObjCClass;
                  demangled_is_synthesized = true;
  
                } else if (symbol_name_ref.startswith(
                               g_objc_v2_prefix_metaclass)) {
                  symbol_name_non_abi_mangled = symbol_name + 1;
                  symbol_name = symbol_name + g_objc_v2_prefix_metaclass.size();
                  type = eSymbolTypeObjCMetaClass;
                  demangled_is_synthesized = true;
                } else if (symbol_name_ref.startswith(g_objc_v2_prefix_ivar)) {
                  symbol_name_non_abi_mangled = symbol_name + 1;
                  symbol_name = symbol_name + g_objc_v2_prefix_ivar.size();
                  type = eSymbolTypeObjCIVar;
                  demangled_is_synthesized = true;
                }
              } else {
                if (nlist.n_value != 0)
                  symbol_section =
                      section_info.GetSection(nlist.n_sect, nlist.n_value);
                type = eSymbolTypeData;
              }
              break;
  
            case N_FNAME:
              // procedure name (f77 kludge): name,,NO_SECT,0,0
              type = eSymbolTypeCompiler;
              break;
  
            case N_FUN:
              // procedure: name,,n_sect,linenumber,address
              if (symbol_name) {
                type = eSymbolTypeCode;
                symbol_section =
                    section_info.GetSection(nlist.n_sect, nlist.n_value);
  
                N_FUN_addr_to_sym_idx.insert(
                    std::make_pair(nlist.n_value, sym_idx));
                // We use the current number of symbols in the symbol table in
                // lieu of using nlist_idx in case we ever start trimming entries
                // out
                N_FUN_indexes.push_back(sym_idx);
              } else {
                type = eSymbolTypeCompiler;
  
                if (!N_FUN_indexes.empty()) {
                  // Copy the size of the function into the original STAB entry
                  // so we don't have to hunt for it later
                  symtab->SymbolAtIndex(N_FUN_indexes.back())
                      ->SetByteSize(nlist.n_value);
                  N_FUN_indexes.pop_back();
                  // We don't really need the end function STAB as it contains
                  // the size which we already placed with the original symbol,
                  // so don't add it if we want a minimal symbol table
                  add_nlist = false;
                }
              }
              break;
  
            case N_STSYM:
              // static symbol: name,,n_sect,type,address
              N_STSYM_addr_to_sym_idx.insert(
                  std::make_pair(nlist.n_value, sym_idx));
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
              if (symbol_name && symbol_name[0]) {
                type = ObjectFile::GetSymbolTypeFromName(symbol_name + 1,
                                                         eSymbolTypeData);
              }
              break;
  
            case N_LCSYM:
              // .lcomm symbol: name,,n_sect,type,address
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
              type = eSymbolTypeCommonBlock;
              break;
  
            case N_BNSYM:
              // We use the current number of symbols in the symbol table in lieu
              // of using nlist_idx in case we ever start trimming entries out
              // Skip these if we want minimal symbol tables
              add_nlist = false;
              break;
  
            case N_ENSYM:
              // Set the size of the N_BNSYM to the terminating index of this
              // N_ENSYM so that we can always skip the entire symbol if we need
              // to navigate more quickly at the source level when parsing STABS
              // Skip these if we want minimal symbol tables
              add_nlist = false;
              break;
  
            case N_OPT:
              // emitted with gcc2_compiled and in gcc source
              type = eSymbolTypeCompiler;
              break;
  
            case N_RSYM:
              // register sym: name,,NO_SECT,type,register
              type = eSymbolTypeVariable;
              break;
  
            case N_SLINE:
              // src line: 0,,n_sect,linenumber,address
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
              type = eSymbolTypeLineEntry;
              break;
  
            case N_SSYM:
              // structure elt: name,,NO_SECT,type,struct_offset
              type = eSymbolTypeVariableType;
              break;
  
            case N_SO:
              // source file name
              type = eSymbolTypeSourceFile;
              if (symbol_name == NULL) {
                add_nlist = false;
                if (N_SO_index != UINT32_MAX) {
                  // Set the size of the N_SO to the terminating index of this
                  // N_SO so that we can always skip the entire N_SO if we need
                  // to navigate more quickly at the source level when parsing
                  // STABS
                  symbol_ptr = symtab->SymbolAtIndex(N_SO_index);
                  symbol_ptr->SetByteSize(sym_idx);
                  symbol_ptr->SetSizeIsSibling(true);
                }
                N_NSYM_indexes.clear();
                N_INCL_indexes.clear();
                N_BRAC_indexes.clear();
                N_COMM_indexes.clear();
                N_FUN_indexes.clear();
                N_SO_index = UINT32_MAX;
              } else {
                // We use the current number of symbols in the symbol table in
                // lieu of using nlist_idx in case we ever start trimming entries
                // out
                const bool N_SO_has_full_path = symbol_name[0] == '/';
                if (N_SO_has_full_path) {
                  if ((N_SO_index == sym_idx - 1) && ((sym_idx - 1) < num_syms)) {
                    // We have two consecutive N_SO entries where the first
                    // contains a directory and the second contains a full path.
                    sym[sym_idx - 1].GetMangled().SetValue(
                        ConstString(symbol_name), false);
                    m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
                    add_nlist = false;
                  } else {
                    // This is the first entry in a N_SO that contains a
                    // directory or a full path to the source file
                    N_SO_index = sym_idx;
                  }
                } else if ((N_SO_index == sym_idx - 1) &&
                           ((sym_idx - 1) < num_syms)) {
                  // This is usually the second N_SO entry that contains just the
                  // filename, so here we combine it with the first one if we are
                  // minimizing the symbol table
                  const char *so_path =
                      sym[sym_idx - 1]
                          .GetMangled()
                          .GetDemangledName(lldb::eLanguageTypeUnknown)
                          .AsCString();
                  if (so_path && so_path[0]) {
                    std::string full_so_path(so_path);
                    const size_t double_slash_pos = full_so_path.find("//");
                    if (double_slash_pos != std::string::npos) {
                      // The linker has been generating bad N_SO entries with
                      // doubled up paths in the format "%s%s" where the first
                      // string in the DW_AT_comp_dir, and the second is the
                      // directory for the source file so you end up with a path
                      // that looks like "/tmp/src//tmp/src/"
                      FileSpec so_dir(so_path);
                      if (!FileSystem::Instance().Exists(so_dir)) {
                        so_dir.SetFile(&full_so_path[double_slash_pos + 1],
                                       FileSpec::Style::native);
                        if (FileSystem::Instance().Exists(so_dir)) {
                          // Trim off the incorrect path
                          full_so_path.erase(0, double_slash_pos + 1);
                        }
                      }
                    }
                    if (*full_so_path.rbegin() != '/')
                      full_so_path += '/';
                    full_so_path += symbol_name;
                    sym[sym_idx - 1].GetMangled().SetValue(
                        ConstString(full_so_path.c_str()), false);
                    add_nlist = false;
                    m_nlist_idx_to_sym_idx[nlist_idx] = sym_idx - 1;
                  }
                } else {
                  // This could be a relative path to a N_SO
                  N_SO_index = sym_idx;
                }
              }
              break;
  
            case N_OSO:
              // object file name: name,,0,0,st_mtime
              type = eSymbolTypeObjectFile;
              break;
  
            case N_LSYM:
              // local sym: name,,NO_SECT,type,offset
              type = eSymbolTypeLocal;
              break;
  
            //----------------------------------------------------------------------
            // INCL scopes
            //----------------------------------------------------------------------
            case N_BINCL:
              // include file beginning: name,,NO_SECT,0,sum We use the current
              // number of symbols in the symbol table in lieu of using nlist_idx
              // in case we ever start trimming entries out
              N_INCL_indexes.push_back(sym_idx);
              type = eSymbolTypeScopeBegin;
              break;
  
            case N_EINCL:
              // include file end: name,,NO_SECT,0,0
              // Set the size of the N_BINCL to the terminating index of this
              // N_EINCL so that we can always skip the entire symbol if we need
              // to navigate more quickly at the source level when parsing STABS
              if (!N_INCL_indexes.empty()) {
                symbol_ptr = symtab->SymbolAtIndex(N_INCL_indexes.back());
                symbol_ptr->SetByteSize(sym_idx + 1);
                symbol_ptr->SetSizeIsSibling(true);
                N_INCL_indexes.pop_back();
              }
              type = eSymbolTypeScopeEnd;
              break;
  
            case N_SOL:
              // #included file name: name,,n_sect,0,address
              type = eSymbolTypeHeaderFile;
  
              // We currently don't use the header files on darwin
              add_nlist = false;
              break;
  
            case N_PARAMS:
              // compiler parameters: name,,NO_SECT,0,0
              type = eSymbolTypeCompiler;
              break;
  
            case N_VERSION:
              // compiler version: name,,NO_SECT,0,0
              type = eSymbolTypeCompiler;
              break;
  
            case N_OLEVEL:
              // compiler -O level: name,,NO_SECT,0,0
              type = eSymbolTypeCompiler;
              break;
  
            case N_PSYM:
              // parameter: name,,NO_SECT,type,offset
              type = eSymbolTypeVariable;
              break;
  
            case N_ENTRY:
              // alternate entry: name,,n_sect,linenumber,address
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
              type = eSymbolTypeLineEntry;
              break;
  
            //----------------------------------------------------------------------
            // Left and Right Braces
            //----------------------------------------------------------------------
            case N_LBRAC:
              // left bracket: 0,,NO_SECT,nesting level,address We use the
              // current number of symbols in the symbol table in lieu of using
              // nlist_idx in case we ever start trimming entries out
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
              N_BRAC_indexes.push_back(sym_idx);
              type = eSymbolTypeScopeBegin;
              break;
  
            case N_RBRAC:
              // right bracket: 0,,NO_SECT,nesting level,address Set the size of
              // the N_LBRAC to the terminating index of this N_RBRAC so that we
              // can always skip the entire symbol if we need to navigate more
              // quickly at the source level when parsing STABS
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
              if (!N_BRAC_indexes.empty()) {
                symbol_ptr = symtab->SymbolAtIndex(N_BRAC_indexes.back());
                symbol_ptr->SetByteSize(sym_idx + 1);
                symbol_ptr->SetSizeIsSibling(true);
                N_BRAC_indexes.pop_back();
              }
              type = eSymbolTypeScopeEnd;
              break;
  
            case N_EXCL:
              // deleted include file: name,,NO_SECT,0,sum
              type = eSymbolTypeHeaderFile;
              break;
  
            //----------------------------------------------------------------------
            // COMM scopes
            //----------------------------------------------------------------------
            case N_BCOMM:
              // begin common: name,,NO_SECT,0,0
              // We use the current number of symbols in the symbol table in lieu
              // of using nlist_idx in case we ever start trimming entries out
              type = eSymbolTypeScopeBegin;
              N_COMM_indexes.push_back(sym_idx);
              break;
  
            case N_ECOML:
              // end common (local name): 0,,n_sect,0,address
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
              LLVM_FALLTHROUGH;
  
            case N_ECOMM:
              // end common: name,,n_sect,0,0
              // Set the size of the N_BCOMM to the terminating index of this
              // N_ECOMM/N_ECOML so that we can always skip the entire symbol if
              // we need to navigate more quickly at the source level when
              // parsing STABS
              if (!N_COMM_indexes.empty()) {
                symbol_ptr = symtab->SymbolAtIndex(N_COMM_indexes.back());
                symbol_ptr->SetByteSize(sym_idx + 1);
                symbol_ptr->SetSizeIsSibling(true);
                N_COMM_indexes.pop_back();
              }
              type = eSymbolTypeScopeEnd;
              break;
  
            case N_LENG:
              // second stab entry with length information
              type = eSymbolTypeAdditional;
              break;
  
            case N_AST:
              // A path to a compiler AST file
              type = eSymbolTypeASTFile;
              break;
  
            default:
              break;
            }
          } else {
            // uint8_t n_pext    = N_PEXT & nlist.n_type;
            uint8_t n_type = N_TYPE & nlist.n_type;
            sym[sym_idx].SetExternal((N_EXT & nlist.n_type) != 0);
  
            switch (n_type) {
            case N_INDR: {
              const char *reexport_name_cstr =
                  strtab_data.PeekCStr(nlist.n_value);
              if (reexport_name_cstr && reexport_name_cstr[0]) {
                type = eSymbolTypeReExported;
                ConstString reexport_name(
                    reexport_name_cstr +
                    ((reexport_name_cstr[0] == '_') ? 1 : 0));
                sym[sym_idx].SetReExportedSymbolName(reexport_name);
                set_value = false;
                reexport_shlib_needs_fixup[sym_idx] = reexport_name;
                indirect_symbol_names.insert(
                    ConstString(symbol_name + ((symbol_name[0] == '_') ? 1 : 0)));
              } else
                type = eSymbolTypeUndefined;
            } break;
  
            case N_UNDF:
              if (symbol_name && symbol_name[0]) {
                ConstString undefined_name(symbol_name +
                                           ((symbol_name[0] == '_') ? 1 : 0));
                undefined_name_to_desc[undefined_name] = nlist.n_desc;
              }
              LLVM_FALLTHROUGH;
  
            case N_PBUD:
              type = eSymbolTypeUndefined;
              break;
  
            case N_ABS:
              type = eSymbolTypeAbsolute;
              break;
  
            case N_SECT: {
              symbol_section =
                  section_info.GetSection(nlist.n_sect, nlist.n_value);
  
              if (!symbol_section) {
                // TODO: warn about this?
                add_nlist = false;
                break;
              }
  
              if (TEXT_eh_frame_sectID == nlist.n_sect) {
                type = eSymbolTypeException;
              } else {
                uint32_t section_type = symbol_section->Get() & SECTION_TYPE;
  
                switch (section_type) {
                case S_CSTRING_LITERALS:
                  type = eSymbolTypeData;
                  break; // section with only literal C strings
                case S_4BYTE_LITERALS:
                  type = eSymbolTypeData;
                  break; // section with only 4 byte literals
                case S_8BYTE_LITERALS:
                  type = eSymbolTypeData;
                  break; // section with only 8 byte literals
                case S_LITERAL_POINTERS:
                  type = eSymbolTypeTrampoline;
                  break; // section with only pointers to literals
                case S_NON_LAZY_SYMBOL_POINTERS:
                  type = eSymbolTypeTrampoline;
                  break; // section with only non-lazy symbol pointers
                case S_LAZY_SYMBOL_POINTERS:
                  type = eSymbolTypeTrampoline;
                  break; // section with only lazy symbol pointers
                case S_SYMBOL_STUBS:
                  type = eSymbolTypeTrampoline;
                  break; // section with only symbol stubs, byte size of stub in
                         // the reserved2 field
                case S_MOD_INIT_FUNC_POINTERS:
                  type = eSymbolTypeCode;
                  break; // section with only function pointers for initialization
                case S_MOD_TERM_FUNC_POINTERS:
                  type = eSymbolTypeCode;
                  break; // section with only function pointers for termination
                case S_INTERPOSING:
                  type = eSymbolTypeTrampoline;
                  break; // section with only pairs of function pointers for
                         // interposing
                case S_16BYTE_LITERALS:
                  type = eSymbolTypeData;
                  break; // section with only 16 byte literals
                case S_DTRACE_DOF:
                  type = eSymbolTypeInstrumentation;
                  break;
                case S_LAZY_DYLIB_SYMBOL_POINTERS:
                  type = eSymbolTypeTrampoline;
                  break;
                default:
                  switch (symbol_section->GetType()) {
                  case lldb::eSectionTypeCode:
                    type = eSymbolTypeCode;
                    break;
                  case eSectionTypeData:
                  case eSectionTypeDataCString:         // Inlined C string data
                  case eSectionTypeDataCStringPointers: // Pointers to C string
                                                        // data
                  case eSectionTypeDataSymbolAddress:   // Address of a symbol in
                                                        // the symbol table
                  case eSectionTypeData4:
                  case eSectionTypeData8:
                  case eSectionTypeData16:
                    type = eSymbolTypeData;
                    break;
                  default:
                    break;
                  }
                  break;
                }
  
                if (type == eSymbolTypeInvalid) {
                  const char *symbol_sect_name =
                      symbol_section->GetName().AsCString();
                  if (symbol_section->IsDescendant(text_section_sp.get())) {
                    if (symbol_section->IsClear(S_ATTR_PURE_INSTRUCTIONS |
                                                S_ATTR_SELF_MODIFYING_CODE |
                                                S_ATTR_SOME_INSTRUCTIONS))
                      type = eSymbolTypeData;
                    else
                      type = eSymbolTypeCode;
                  } else if (symbol_section->IsDescendant(
                                 data_section_sp.get()) ||
                             symbol_section->IsDescendant(
                                 data_dirty_section_sp.get()) ||
                             symbol_section->IsDescendant(
                                 data_const_section_sp.get())) {
                    if (symbol_sect_name &&
                        ::strstr(symbol_sect_name, "__objc") ==
                            symbol_sect_name) {
                      type = eSymbolTypeRuntime;
  
                      if (symbol_name) {
                        llvm::StringRef symbol_name_ref(symbol_name);
                        if (symbol_name_ref.startswith("_OBJC_")) {
                          static const llvm::StringRef g_objc_v2_prefix_class(
                              "_OBJC_CLASS_$_");
                          static const llvm::StringRef g_objc_v2_prefix_metaclass(
                              "_OBJC_METACLASS_$_");
                          static const llvm::StringRef g_objc_v2_prefix_ivar(
                              "_OBJC_IVAR_$_");
                          if (symbol_name_ref.startswith(
                                  g_objc_v2_prefix_class)) {
                            symbol_name_non_abi_mangled = symbol_name + 1;
                            symbol_name =
                                symbol_name + g_objc_v2_prefix_class.size();
                            type = eSymbolTypeObjCClass;
                            demangled_is_synthesized = true;
                          } else if (symbol_name_ref.startswith(
                                         g_objc_v2_prefix_metaclass)) {
                            symbol_name_non_abi_mangled = symbol_name + 1;
                            symbol_name =
                                symbol_name + g_objc_v2_prefix_metaclass.size();
                            type = eSymbolTypeObjCMetaClass;
                            demangled_is_synthesized = true;
                          } else if (symbol_name_ref.startswith(
                                         g_objc_v2_prefix_ivar)) {
                            symbol_name_non_abi_mangled = symbol_name + 1;
                            symbol_name =
                                symbol_name + g_objc_v2_prefix_ivar.size();
                            type = eSymbolTypeObjCIVar;
                            demangled_is_synthesized = true;
                          }
                        }
                      }
                    } else if (symbol_sect_name &&
                               ::strstr(symbol_sect_name, "__gcc_except_tab") ==
                                   symbol_sect_name) {
                      type = eSymbolTypeException;
                    } else {
                      type = eSymbolTypeData;
                    }
                  } else if (symbol_sect_name &&
                             ::strstr(symbol_sect_name, "__IMPORT") ==
                                 symbol_sect_name) {
                    type = eSymbolTypeTrampoline;
                  } else if (symbol_section->IsDescendant(
                                 objc_section_sp.get())) {
                    type = eSymbolTypeRuntime;
                    if (symbol_name && symbol_name[0] == '.') {
                      llvm::StringRef symbol_name_ref(symbol_name);
                      static const llvm::StringRef g_objc_v1_prefix_class(
                          ".objc_class_name_");
                      if (symbol_name_ref.startswith(g_objc_v1_prefix_class)) {
                        symbol_name_non_abi_mangled = symbol_name;
                        symbol_name = symbol_name + g_objc_v1_prefix_class.size();
                        type = eSymbolTypeObjCClass;
                        demangled_is_synthesized = true;
                      }
                    }
                  }
                }
              }
            } break;
            }
          }
  
          if (add_nlist) {
            uint64_t symbol_value = nlist.n_value;
  
            if (symbol_name_non_abi_mangled) {
              sym[sym_idx].GetMangled().SetMangledName(
                  ConstString(symbol_name_non_abi_mangled));
              sym[sym_idx].GetMangled().SetDemangledName(
                  ConstString(symbol_name));
            } else {
              bool symbol_name_is_mangled = false;
  
              if (symbol_name && symbol_name[0] == '_') {
                symbol_name_is_mangled = symbol_name[1] == '_';
                symbol_name_is_mangled |= symbol_name[1] == '$';
                symbol_name++; // Skip the leading underscore
              }
  
              if (symbol_name) {
                ConstString const_symbol_name(symbol_name);
                sym[sym_idx].GetMangled().SetValue(const_symbol_name,
                                                   symbol_name_is_mangled);
              }
            }
  
            if (is_gsym) {
              const char *gsym_name = sym[sym_idx]
                                          .GetMangled()
                                          .GetName(lldb::eLanguageTypeUnknown,
                                                   Mangled::ePreferMangled)
                                          .GetCString();
              if (gsym_name)
                N_GSYM_name_to_sym_idx[gsym_name] = sym_idx;
            }
  
            if (symbol_section) {
              // Keep track of the symbols we added by address in case we have
              // other sources
              // for symbols where we only want to add a symbol if it isn't
              // already in the
              // symbol table.
              symbol_file_addresses.insert(nlist.n_value);
  
              const addr_t section_file_addr = symbol_section->GetFileAddress();
              if (symbol_byte_size == 0 && function_starts_count > 0) {
                addr_t symbol_lookup_file_addr = nlist.n_value;
                // Do an exact address match for non-ARM addresses, else get the
                // closest since the symbol might be a thumb symbol which has an
                // address with bit zero set
                FunctionStarts::Entry *func_start_entry =
                    function_starts.FindEntry(symbol_lookup_file_addr, !is_arm);
                if (is_arm && func_start_entry) {
                  // Verify that the function start address is the symbol address
                  // (ARM) or the symbol address + 1 (thumb)
                  if (func_start_entry->addr != symbol_lookup_file_addr &&
                      func_start_entry->addr != (symbol_lookup_file_addr + 1)) {
                    // Not the right entry, NULL it out...
                    func_start_entry = NULL;
                  }
                }
                if (func_start_entry) {
                  func_start_entry->data = true;
  
                  addr_t symbol_file_addr = func_start_entry->addr;
                  if (is_arm)
                    symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
  
                  const FunctionStarts::Entry *next_func_start_entry =
                      function_starts.FindNextEntry(func_start_entry);
                  const addr_t section_end_file_addr =
                      section_file_addr + symbol_section->GetByteSize();
                  if (next_func_start_entry) {
                    addr_t next_symbol_file_addr = next_func_start_entry->addr;
                    // Be sure the clear the Thumb address bit when we calculate
                    // the size from the current and next address
                    if (is_arm)
                      next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
                    symbol_byte_size = std::min<lldb::addr_t>(
                        next_symbol_file_addr - symbol_file_addr,
                        section_end_file_addr - symbol_file_addr);
                  } else {
                    symbol_byte_size = section_end_file_addr - symbol_file_addr;
                  }
                }
              }
              symbol_value -= section_file_addr;
            }
  
            if (!is_debug) {
              if (type == eSymbolTypeCode) {
                // See if we can find a N_FUN entry for any code symbols. If we
                // do find a match, and the name matches, then we can merge the
                // two into just the function symbol to avoid duplicate entries
                // in the symbol table
                std::pair<ValueToSymbolIndexMap::const_iterator,
                          ValueToSymbolIndexMap::const_iterator>
                    range;
                range = N_FUN_addr_to_sym_idx.equal_range(nlist.n_value);
                if (range.first != range.second) {
                  bool found_it = false;
                  for (ValueToSymbolIndexMap::const_iterator pos = range.first;
                       pos != range.second; ++pos) {
                    if (sym[sym_idx].GetMangled().GetName(
                            lldb::eLanguageTypeUnknown,
                            Mangled::ePreferMangled) ==
                        sym[pos->second].GetMangled().GetName(
                            lldb::eLanguageTypeUnknown,
                            Mangled::ePreferMangled)) {
                      m_nlist_idx_to_sym_idx[nlist_idx] = pos->second;
                      // We just need the flags from the linker symbol, so put
                      // these flags into the N_FUN flags to avoid duplicate
                      // symbols in the symbol table
                      sym[pos->second].SetExternal(sym[sym_idx].IsExternal());
                      sym[pos->second].SetFlags(nlist.n_type << 16 |
                                                nlist.n_desc);
                      if (resolver_addresses.find(nlist.n_value) !=
                          resolver_addresses.end())
                        sym[pos->second].SetType(eSymbolTypeResolver);
                      sym[sym_idx].Clear();
                      found_it = true;
                      break;
                    }
                  }
                  if (found_it)
                    continue;
                } else {
                  if (resolver_addresses.find(nlist.n_value) !=
                      resolver_addresses.end())
                    type = eSymbolTypeResolver;
                }
              } else if (type == eSymbolTypeData ||
                         type == eSymbolTypeObjCClass ||
                         type == eSymbolTypeObjCMetaClass ||
                         type == eSymbolTypeObjCIVar) {
                // See if we can find a N_STSYM entry for any data symbols. If we
                // do find a match, and the name matches, then we can merge the
                // two into just the Static symbol to avoid duplicate entries in
                // the symbol table
                std::pair<ValueToSymbolIndexMap::const_iterator,
                          ValueToSymbolIndexMap::const_iterator>
                    range;
                range = N_STSYM_addr_to_sym_idx.equal_range(nlist.n_value);
                if (range.first != range.second) {
                  bool found_it = false;
                  for (ValueToSymbolIndexMap::const_iterator pos = range.first;
                       pos != range.second; ++pos) {
                    if (sym[sym_idx].GetMangled().GetName(
                            lldb::eLanguageTypeUnknown,
                            Mangled::ePreferMangled) ==
                        sym[pos->second].GetMangled().GetName(
                            lldb::eLanguageTypeUnknown,
                            Mangled::ePreferMangled)) {
                      m_nlist_idx_to_sym_idx[nlist_idx] = pos->second;
                      // We just need the flags from the linker symbol, so put
                      // these flags into the N_STSYM flags to avoid duplicate
                      // symbols in the symbol table
                      sym[pos->second].SetExternal(sym[sym_idx].IsExternal());
                      sym[pos->second].SetFlags(nlist.n_type << 16 |
                                                nlist.n_desc);
                      sym[sym_idx].Clear();
                      found_it = true;
                      break;
                    }
                  }
                  if (found_it)
                    continue;
                } else {
                  // Combine N_GSYM stab entries with the non stab symbol
                  const char *gsym_name = sym[sym_idx]
                                              .GetMangled()
                                              .GetName(lldb::eLanguageTypeUnknown,
                                                       Mangled::ePreferMangled)
                                              .GetCString();
                  if (gsym_name) {
                    ConstNameToSymbolIndexMap::const_iterator pos =
                        N_GSYM_name_to_sym_idx.find(gsym_name);
                    if (pos != N_GSYM_name_to_sym_idx.end()) {
                      const uint32_t GSYM_sym_idx = pos->second;
                      m_nlist_idx_to_sym_idx[nlist_idx] = GSYM_sym_idx;
                      // Copy the address, because often the N_GSYM address has
                      // an invalid address of zero when the global is a common
                      // symbol
                      sym[GSYM_sym_idx].GetAddressRef().SetSection(
                          symbol_section);
                      sym[GSYM_sym_idx].GetAddressRef().SetOffset(symbol_value);
                      // We just need the flags from the linker symbol, so put
                      // these flags into the N_GSYM flags to avoid duplicate
                      // symbols in the symbol table
                      sym[GSYM_sym_idx].SetFlags(nlist.n_type << 16 |
                                                 nlist.n_desc);
                      sym[sym_idx].Clear();
                      continue;
                    }
                  }
                }
              }
            }
  
            sym[sym_idx].SetID(nlist_idx);
            sym[sym_idx].SetType(type);
            if (set_value) {
              sym[sym_idx].GetAddressRef().SetSection(symbol_section);
              sym[sym_idx].GetAddressRef().SetOffset(symbol_value);
            }
            sym[sym_idx].SetFlags(nlist.n_type << 16 | nlist.n_desc);
  
            if (symbol_byte_size > 0)
              sym[sym_idx].SetByteSize(symbol_byte_size);
  
            if (demangled_is_synthesized)
              sym[sym_idx].SetDemangledNameIsSynthesized(true);
  
            ++sym_idx;
          } else {
            sym[sym_idx].Clear();
          }
        }
  
        for (const auto &pos : reexport_shlib_needs_fixup) {
          const auto undef_pos = undefined_name_to_desc.find(pos.second);
          if (undef_pos != undefined_name_to_desc.end()) {
            const uint8_t dylib_ordinal =
                llvm::MachO::GET_LIBRARY_ORDINAL(undef_pos->second);
            if (dylib_ordinal > 0 && dylib_ordinal < dylib_files.GetSize())
              sym[pos.first].SetReExportedSymbolSharedLibrary(
                  dylib_files.GetFileSpecAtIndex(dylib_ordinal - 1));
          }
        }
      }
  
      uint32_t synthetic_sym_id = symtab_load_command.nsyms;
  
      // Check the trie for any symbols that are in the trie only and make symbols
      // for those
      if (!trie_entries.empty()) {
        for (const auto &e : trie_entries) {
          // Don't handle the re-exported symbols here, just the symbols that were
          // in the trie only
          if (e.entry.name && !e.entry.import_name &&
              symbol_file_addresses.find(e.entry.address) ==
                  symbol_file_addresses.end()) {
            SectionSP symbol_section =
                section_list->FindSectionContainingFileAddress(e.entry.address);
            if (symbol_section) {
              const addr_t section_file_addr = symbol_section->GetFileAddress();
  
              // Keep track of the symbols we added by address in case we have
              // other sources
              // for symbols where we only want to add a symbol if it isn't
              // already in the
              // symbol table.
              symbol_file_addresses.insert(e.entry.address);
  
              if (e.entry.address >= section_file_addr) {
                const uint64_t symbol_value = e.entry.address - section_file_addr;
  
                const char *symbol_name = e.entry.name.GetCString();
                const char *symbol_name_non_abi_mangled = nullptr;
                bool demangled_is_synthesized = false;
                SymbolType type = GetSymbolType(
                    symbol_name, symbol_name_non_abi_mangled,
                    demangled_is_synthesized, text_section_sp, data_section_sp,
                    data_dirty_section_sp, data_const_section_sp, objc_section_sp,
                    symbol_section);
  
                // Make a synthetic symbol to describe re-exported symbol.
                if (sym_idx >= num_syms)
                  sym = symtab->Resize(++num_syms);
                sym[sym_idx].SetID(synthetic_sym_id++);
                sym[sym_idx].GetMangled() = Mangled(e.entry.name, true);
                sym[sym_idx].SetType(type);
                sym[sym_idx].SetIsSynthetic(true);
                sym[sym_idx].GetAddressRef().SetSection(symbol_section);
                sym[sym_idx].GetAddressRef().SetOffset(symbol_value);
                if (demangled_is_synthesized)
                  sym[sym_idx].SetDemangledNameIsSynthesized(true);
  
                ++sym_idx;
              }
            }
          }
        }
      }
  
      if (function_starts_count > 0) {
        uint32_t num_synthetic_function_symbols = 0;
        for (i = 0; i < function_starts_count; ++i) {
          if (!function_starts.GetEntryRef(i).data)
            ++num_synthetic_function_symbols;
        }
  
        if (num_synthetic_function_symbols > 0) {
          if (num_syms < sym_idx + num_synthetic_function_symbols) {
            num_syms = sym_idx + num_synthetic_function_symbols;
            sym = symtab->Resize(num_syms);
          }
          for (i = 0; i < function_starts_count; ++i) {
            const FunctionStarts::Entry *func_start_entry =
                function_starts.GetEntryAtIndex(i);
            if (!func_start_entry->data) {
              addr_t symbol_file_addr = func_start_entry->addr;
              uint32_t symbol_flags = 0;
              if (is_arm) {
                if (symbol_file_addr & 1)
                  symbol_flags = MACHO_NLIST_ARM_SYMBOL_IS_THUMB;
                symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
              }
              Address symbol_addr;
              if (module_sp->ResolveFileAddress(symbol_file_addr, symbol_addr)) {
                SectionSP symbol_section(symbol_addr.GetSection());
                uint32_t symbol_byte_size = 0;
                if (symbol_section) {
                  const addr_t section_file_addr =
                      symbol_section->GetFileAddress();
                  const FunctionStarts::Entry *next_func_start_entry =
                      function_starts.FindNextEntry(func_start_entry);
                  const addr_t section_end_file_addr =
                      section_file_addr + symbol_section->GetByteSize();
                  if (next_func_start_entry) {
                    addr_t next_symbol_file_addr = next_func_start_entry->addr;
                    if (is_arm)
                      next_symbol_file_addr &= THUMB_ADDRESS_BIT_MASK;
                    symbol_byte_size = std::min<lldb::addr_t>(
                        next_symbol_file_addr - symbol_file_addr,
                        section_end_file_addr - symbol_file_addr);
                  } else {
                    symbol_byte_size = section_end_file_addr - symbol_file_addr;
                  }
                  sym[sym_idx].SetID(synthetic_sym_id++);
                  sym[sym_idx].GetMangled().SetDemangledName(
                      GetNextSyntheticSymbolName());
                  sym[sym_idx].SetType(eSymbolTypeCode);
                  sym[sym_idx].SetIsSynthetic(true);
                  sym[sym_idx].GetAddressRef() = symbol_addr;
                  if (symbol_flags)
                    sym[sym_idx].SetFlags(symbol_flags);
                  if (symbol_byte_size)
                    sym[sym_idx].SetByteSize(symbol_byte_size);
                  ++sym_idx;
                }
              }
            }
          }
        }
      }
  
      // Trim our symbols down to just what we ended up with after removing any
      // symbols.
      if (sym_idx < num_syms) {
        num_syms = sym_idx;
        sym = symtab->Resize(num_syms);
      }
  
      // Now synthesize indirect symbols
      if (m_dysymtab.nindirectsyms != 0) {
        if (indirect_symbol_index_data.GetByteSize()) {
          NListIndexToSymbolIndexMap::const_iterator end_index_pos =
              m_nlist_idx_to_sym_idx.end();
  
          for (uint32_t sect_idx = 1; sect_idx < m_mach_sections.size();
               ++sect_idx) {
            if ((m_mach_sections[sect_idx].flags & SECTION_TYPE) ==
                S_SYMBOL_STUBS) {
              uint32_t symbol_stub_byte_size =
                  m_mach_sections[sect_idx].reserved2;
              if (symbol_stub_byte_size == 0)
                continue;
  
              const uint32_t num_symbol_stubs =
                  m_mach_sections[sect_idx].size / symbol_stub_byte_size;
  
              if (num_symbol_stubs == 0)
                continue;
  
              const uint32_t symbol_stub_index_offset =
                  m_mach_sections[sect_idx].reserved1;
              for (uint32_t stub_idx = 0; stub_idx < num_symbol_stubs;
                   ++stub_idx) {
                const uint32_t symbol_stub_index =
                    symbol_stub_index_offset + stub_idx;
                const lldb::addr_t symbol_stub_addr =
                    m_mach_sections[sect_idx].addr +
                    (stub_idx * symbol_stub_byte_size);
                lldb::offset_t symbol_stub_offset = symbol_stub_index * 4;
                if (indirect_symbol_index_data.ValidOffsetForDataOfSize(
                        symbol_stub_offset, 4)) {
                  const uint32_t stub_sym_id =
                      indirect_symbol_index_data.GetU32(&symbol_stub_offset);
                  if (stub_sym_id & (INDIRECT_SYMBOL_ABS | INDIRECT_SYMBOL_LOCAL))
                    continue;
  
                  NListIndexToSymbolIndexMap::const_iterator index_pos =
                      m_nlist_idx_to_sym_idx.find(stub_sym_id);
                  Symbol *stub_symbol = NULL;
                  if (index_pos != end_index_pos) {
                    // We have a remapping from the original nlist index to a
                    // current symbol index, so just look this up by index
                    stub_symbol = symtab->SymbolAtIndex(index_pos->second);
                  } else {
                    // We need to lookup a symbol using the original nlist symbol
                    // index since this index is coming from the S_SYMBOL_STUBS
                    stub_symbol = symtab->FindSymbolByID(stub_sym_id);
                  }
  
                  if (stub_symbol) {
                    Address so_addr(symbol_stub_addr, section_list);
  
                    if (stub_symbol->GetType() == eSymbolTypeUndefined) {
                      // Change the external symbol into a trampoline that makes
                      // sense These symbols were N_UNDF N_EXT, and are useless
                      // to us, so we can re-use them so we don't have to make up
                      // a synthetic symbol for no good reason.
                      if (resolver_addresses.find(symbol_stub_addr) ==
                          resolver_addresses.end())
                        stub_symbol->SetType(eSymbolTypeTrampoline);
                      else
                        stub_symbol->SetType(eSymbolTypeResolver);
                      stub_symbol->SetExternal(false);
                      stub_symbol->GetAddressRef() = so_addr;
                      stub_symbol->SetByteSize(symbol_stub_byte_size);
                    } else {
                      // Make a synthetic symbol to describe the trampoline stub
                      Mangled stub_symbol_mangled_name(stub_symbol->GetMangled());
                      if (sym_idx >= num_syms) {
                        sym = symtab->Resize(++num_syms);
                        stub_symbol = NULL; // this pointer no longer valid
                      }
                      sym[sym_idx].SetID(synthetic_sym_id++);
                      sym[sym_idx].GetMangled() = stub_symbol_mangled_name;
                      if (resolver_addresses.find(symbol_stub_addr) ==
                          resolver_addresses.end())
                        sym[sym_idx].SetType(eSymbolTypeTrampoline);
                      else
                        sym[sym_idx].SetType(eSymbolTypeResolver);
                      sym[sym_idx].SetIsSynthetic(true);
                      sym[sym_idx].GetAddressRef() = so_addr;
                      sym[sym_idx].SetByteSize(symbol_stub_byte_size);
                      ++sym_idx;
                    }
                  } else {
                    if (log)
                      log->Warning("symbol stub referencing symbol table symbol "
                                   "%u that isn't in our minimal symbol table, "
                                   "fix this!!!",
                                   stub_sym_id);
                  }
                }
              }
            }
          }
        }
      }
  
      if (!trie_entries.empty()) {
        for (const auto &e : trie_entries) {
          if (e.entry.import_name) {
            // Only add indirect symbols from the Trie entries if we didn't have
            // a N_INDR nlist entry for this already
            if (indirect_symbol_names.find(e.entry.name) ==
                indirect_symbol_names.end()) {
              // Make a synthetic symbol to describe re-exported symbol.
              if (sym_idx >= num_syms)
                sym = symtab->Resize(++num_syms);
              sym[sym_idx].SetID(synthetic_sym_id++);
              sym[sym_idx].GetMangled() = Mangled(e.entry.name);
              sym[sym_idx].SetType(eSymbolTypeReExported);
              sym[sym_idx].SetIsSynthetic(true);
              sym[sym_idx].SetReExportedSymbolName(e.entry.import_name);
              if (e.entry.other > 0 && e.entry.other <= dylib_files.GetSize()) {
                sym[sym_idx].SetReExportedSymbolSharedLibrary(
                    dylib_files.GetFileSpecAtIndex(e.entry.other - 1));
              }
              ++sym_idx;
            }
          }
        }
      }
  
      //        StreamFile s(stdout, false);
      //        s.Printf ("Symbol table before CalculateSymbolSizes():\n");
      //        symtab->Dump(&s, NULL, eSortOrderNone);
      // Set symbol byte sizes correctly since mach-o nlist entries don't have
      // sizes
      symtab->CalculateSymbolSizes();
  
      //        s.Printf ("Symbol table after CalculateSymbolSizes():\n");
      //        symtab->Dump(&s, NULL, eSortOrderNone);
  
      return symtab->GetNumSymbols();
    }
    return 0;
  }
  
  void ObjectFileMachO::Dump(Stream *s) {
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      s->Printf("%p: ", static_cast<void *>(this));
      s->Indent();
      if (m_header.magic == MH_MAGIC_64 || m_header.magic == MH_CIGAM_64)
        s->PutCString("ObjectFileMachO64");
      else
        s->PutCString("ObjectFileMachO32");
  
      ArchSpec header_arch = GetArchitecture();
  
      *s << ", file = '" << m_file
         << "', triple = " << header_arch.GetTriple().getTriple() << "\n";
  
      SectionList *sections = GetSectionList();
      if (sections)
        sections->Dump(s, NULL, true, UINT32_MAX);
  
      if (m_symtab_up)
        m_symtab_up->Dump(s, NULL, eSortOrderNone);
    }
  }
  
  bool ObjectFileMachO::GetUUID(const llvm::MachO::mach_header &header,
                                const lldb_private::DataExtractor &data,
                                lldb::offset_t lc_offset,
                                lldb_private::UUID &uuid) {
    uint32_t i;
    struct uuid_command load_cmd;
  
    lldb::offset_t offset = lc_offset;
    for (i = 0; i < header.ncmds; ++i) {
      const lldb::offset_t cmd_offset = offset;
      if (data.GetU32(&offset, &load_cmd, 2) == NULL)
        break;
  
      if (load_cmd.cmd == LC_UUID) {
        const uint8_t *uuid_bytes = data.PeekData(offset, 16);
  
        if (uuid_bytes) {
          // OpenCL on Mac OS X uses the same UUID for each of its object files.
          // We pretend these object files have no UUID to prevent crashing.
  
          const uint8_t opencl_uuid[] = {0x8c, 0x8e, 0xb3, 0x9b, 0x3b, 0xa8,
                                         0x4b, 0x16, 0xb6, 0xa4, 0x27, 0x63,
                                         0xbb, 0x14, 0xf0, 0x0d};
  
          if (!memcmp(uuid_bytes, opencl_uuid, 16))
            return false;
  
          uuid = UUID::fromOptionalData(uuid_bytes, 16);
          return true;
        }
        return false;
      }
      offset = cmd_offset + load_cmd.cmdsize;
    }
    return false;
  }
  
  static llvm::StringRef GetOSName(uint32_t cmd) {
    switch (cmd) {
    case llvm::MachO::LC_VERSION_MIN_IPHONEOS:
      return llvm::Triple::getOSTypeName(llvm::Triple::IOS);
    case llvm::MachO::LC_VERSION_MIN_MACOSX:
      return llvm::Triple::getOSTypeName(llvm::Triple::MacOSX);
    case llvm::MachO::LC_VERSION_MIN_TVOS:
      return llvm::Triple::getOSTypeName(llvm::Triple::TvOS);
    case llvm::MachO::LC_VERSION_MIN_WATCHOS:
      return llvm::Triple::getOSTypeName(llvm::Triple::WatchOS);
    default:
      llvm_unreachable("unexpected LC_VERSION load command");
    }
  }
  
  namespace {
    struct OSEnv {
      llvm::StringRef os_type;
      llvm::StringRef environment;
      OSEnv(uint32_t cmd) {
        switch (cmd) {
        case PLATFORM_MACOS:
          os_type = llvm::Triple::getOSTypeName(llvm::Triple::MacOSX);
          return;
        case PLATFORM_IOS:
          os_type = llvm::Triple::getOSTypeName(llvm::Triple::IOS);
          return;
        case PLATFORM_TVOS:
          os_type = llvm::Triple::getOSTypeName(llvm::Triple::TvOS);
          return;
        case PLATFORM_WATCHOS:
          os_type = llvm::Triple::getOSTypeName(llvm::Triple::WatchOS);
          return;
  // NEED_BRIDGEOS_TRIPLE      case PLATFORM_BRIDGEOS:
  // NEED_BRIDGEOS_TRIPLE        os_type = llvm::Triple::getOSTypeName(llvm::Triple::BridgeOS);
  // NEED_BRIDGEOS_TRIPLE        return;
  #if defined (PLATFORM_IOSSIMULATOR) && defined (PLATFORM_TVOSSIMULATOR) && defined (PLATFORM_WATCHOSSIMULATOR)
        case PLATFORM_IOSSIMULATOR:
          os_type = llvm::Triple::getOSTypeName(llvm::Triple::IOS);
          environment =
              llvm::Triple::getEnvironmentTypeName(llvm::Triple::Simulator);
          return;
        case PLATFORM_TVOSSIMULATOR:
          os_type = llvm::Triple::getOSTypeName(llvm::Triple::TvOS);
          environment =
              llvm::Triple::getEnvironmentTypeName(llvm::Triple::Simulator);
          return;
        case PLATFORM_WATCHOSSIMULATOR:
          os_type = llvm::Triple::getOSTypeName(llvm::Triple::WatchOS);
          environment =
              llvm::Triple::getEnvironmentTypeName(llvm::Triple::Simulator);
          return;
  #endif
        default: {
          Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS |
                                                          LIBLLDB_LOG_PROCESS));
          if (log)
            log->Printf("unsupported platform in LC_BUILD_VERSION");
        }
        }
      }
    };
  
    struct MinOS {
      uint32_t major_version, minor_version, patch_version;
      MinOS(uint32_t version)
          : major_version(version >> 16),
            minor_version((version >> 8) & 0xffu),
            patch_version(version & 0xffu) {}
    };
  } // namespace
  
  ArchSpec
  ObjectFileMachO::GetArchitecture(const llvm::MachO::mach_header &header,
                                   const lldb_private::DataExtractor &data,
                                   lldb::offset_t lc_offset) {
    ArchSpec arch;
    arch.SetArchitecture(eArchTypeMachO, header.cputype, header.cpusubtype);
  
    if (arch.IsValid()) {
      llvm::Triple &triple = arch.GetTriple();
  
      // Set OS to an unspecified unknown or a "*" so it can match any OS
      triple.setOS(llvm::Triple::UnknownOS);
      triple.setOSName(llvm::StringRef());
  
      if (header.filetype == MH_PRELOAD) {
        if (header.cputype == CPU_TYPE_ARM) {
          // If this is a 32-bit arm binary, and it's a standalone binary, force
          // the Vendor to Apple so we don't accidentally pick up the generic
          // armv7 ABI at runtime.  Apple's armv7 ABI always uses r7 for the
          // frame pointer register; most other armv7 ABIs use a combination of
          // r7 and r11.
          triple.setVendor(llvm::Triple::Apple);
        } else {
          // Set vendor to an unspecified unknown or a "*" so it can match any
          // vendor This is required for correct behavior of EFI debugging on
          // x86_64
          triple.setVendor(llvm::Triple::UnknownVendor);
          triple.setVendorName(llvm::StringRef());
        }
        return arch;
      } else {
        struct load_command load_cmd;
        llvm::SmallString<16> os_name;
        llvm::raw_svector_ostream os(os_name);
  
        // See if there is an LC_VERSION_MIN_* load command that can give
        // us the OS type.
        lldb::offset_t offset = lc_offset;
        for (uint32_t i = 0; i < header.ncmds; ++i) {
          const lldb::offset_t cmd_offset = offset;
          if (data.GetU32(&offset, &load_cmd, 2) == NULL)
            break;
  
          struct version_min_command version_min;
          switch (load_cmd.cmd) {
          case llvm::MachO::LC_VERSION_MIN_IPHONEOS:
          case llvm::MachO::LC_VERSION_MIN_MACOSX:
          case llvm::MachO::LC_VERSION_MIN_TVOS:
          case llvm::MachO::LC_VERSION_MIN_WATCHOS: {
            if (load_cmd.cmdsize != sizeof(version_min))
              break;
            if (data.ExtractBytes(cmd_offset, sizeof(version_min),
                                  data.GetByteOrder(), &version_min) == 0)
              break;
            MinOS min_os(version_min.version);
            os << GetOSName(load_cmd.cmd) << min_os.major_version << '.'
               << min_os.minor_version << '.' << min_os.patch_version;
            triple.setOSName(os.str());
            return arch;
          }
          default:
            break;
          }
  
          offset = cmd_offset + load_cmd.cmdsize;
        }
  
        // See if there is an LC_BUILD_VERSION load command that can give
        // us the OS type.
  
        offset = lc_offset;
        for (uint32_t i = 0; i < header.ncmds; ++i) {
          const lldb::offset_t cmd_offset = offset;
          if (data.GetU32(&offset, &load_cmd, 2) == NULL)
            break;
          do {
            if (load_cmd.cmd == llvm::MachO::LC_BUILD_VERSION) {
              struct build_version_command build_version;
              if (load_cmd.cmdsize < sizeof(build_version)) {
                // Malformed load command.
                break;
              }
              if (data.ExtractBytes(cmd_offset, sizeof(build_version),
                                    data.GetByteOrder(), &build_version) == 0)
                break;
              MinOS min_os(build_version.minos);
              OSEnv os_env(build_version.platform);
              if (os_env.os_type.empty())
                break;
              os << os_env.os_type << min_os.major_version << '.'
                 << min_os.minor_version << '.' << min_os.patch_version;
              triple.setOSName(os.str());
              if (!os_env.environment.empty())
                triple.setEnvironmentName(os_env.environment);
              return arch;
            }
          } while (0);
          offset = cmd_offset + load_cmd.cmdsize;
        }
  
        if (header.filetype != MH_KEXT_BUNDLE) {
          // We didn't find a LC_VERSION_MIN load command and this isn't a KEXT
          // so lets not say our Vendor is Apple, leave it as an unspecified
          // unknown
          triple.setVendor(llvm::Triple::UnknownVendor);
          triple.setVendorName(llvm::StringRef());
        }
      }
    }
    return arch;
  }
  
  bool ObjectFileMachO::GetUUID(lldb_private::UUID *uuid) {
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      return GetUUID(m_header, m_data, offset, *uuid);
    }
    return false;
  }
  
  uint32_t ObjectFileMachO::GetDependentModules(FileSpecList &files) {
    uint32_t count = 0;
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      struct load_command load_cmd;
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      std::vector<std::string> rpath_paths;
      std::vector<std::string> rpath_relative_paths;
      std::vector<std::string> at_exec_relative_paths;
      uint32_t i;
      for (i = 0; i < m_header.ncmds; ++i) {
        const uint32_t cmd_offset = offset;
        if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
          break;
  
        switch (load_cmd.cmd) {
        case LC_RPATH:
        case LC_LOAD_DYLIB:
        case LC_LOAD_WEAK_DYLIB:
        case LC_REEXPORT_DYLIB:
        case LC_LOAD_DYLINKER:
        case LC_LOADFVMLIB:
        case LC_LOAD_UPWARD_DYLIB: {
          uint32_t name_offset = cmd_offset + m_data.GetU32(&offset);
          const char *path = m_data.PeekCStr(name_offset);
          if (path) {
            if (load_cmd.cmd == LC_RPATH)
              rpath_paths.push_back(path);
            else {
              if (path[0] == '@') {
                if (strncmp(path, "@rpath", strlen("@rpath")) == 0)
                  rpath_relative_paths.push_back(path + strlen("@rpath"));
                else if (strncmp(path, "@executable_path", 
                         strlen("@executable_path")) == 0)
                  at_exec_relative_paths.push_back(path 
                                                   + strlen("@executable_path"));
              } else {
                FileSpec file_spec(path);
                if (files.AppendIfUnique(file_spec))
                  count++;
              }
            }
          }
        } break;
  
        default:
          break;
        }
        offset = cmd_offset + load_cmd.cmdsize;
      }
  
      FileSpec this_file_spec(m_file);
      FileSystem::Instance().Resolve(this_file_spec);
  
      if (!rpath_paths.empty()) {
        // Fixup all LC_RPATH values to be absolute paths
        std::string loader_path("@loader_path");
        std::string executable_path("@executable_path");
        for (auto &rpath : rpath_paths) {
          if (rpath.find(loader_path) == 0) {
            rpath.erase(0, loader_path.size());
            rpath.insert(0, this_file_spec.GetDirectory().GetCString());
          } else if (rpath.find(executable_path) == 0) {
            rpath.erase(0, executable_path.size());
            rpath.insert(0, this_file_spec.GetDirectory().GetCString());
          }
        }
  
        for (const auto &rpath_relative_path : rpath_relative_paths) {
          for (const auto &rpath : rpath_paths) {
            std::string path = rpath;
            path += rpath_relative_path;
            // It is OK to resolve this path because we must find a file on disk
            // for us to accept it anyway if it is rpath relative.
            FileSpec file_spec(path);
            FileSystem::Instance().Resolve(file_spec);
            if (FileSystem::Instance().Exists(file_spec) &&
                files.AppendIfUnique(file_spec)) {
              count++;
              break;
            }
          }
        }
      }
  
      // We may have @executable_paths but no RPATHS.  Figure those out here.
      // Only do this if this object file is the executable.  We have no way to
      // get back to the actual executable otherwise, so we won't get the right
      // path.
      if (!at_exec_relative_paths.empty() && CalculateType() == eTypeExecutable) {
        FileSpec exec_dir = this_file_spec.CopyByRemovingLastPathComponent();
        for (const auto &at_exec_relative_path : at_exec_relative_paths) {
          FileSpec file_spec = 
              exec_dir.CopyByAppendingPathComponent(at_exec_relative_path);
          if (FileSystem::Instance().Exists(file_spec) &&
              files.AppendIfUnique(file_spec))
            count++;
        }
      }
    }
    return count;
  }
  
  lldb_private::Address ObjectFileMachO::GetEntryPointAddress() {
    // If the object file is not an executable it can't hold the entry point.
    // m_entry_point_address is initialized to an invalid address, so we can just
    // return that. If m_entry_point_address is valid it means we've found it
    // already, so return the cached value.
  
    if (!IsExecutable() || m_entry_point_address.IsValid())
      return m_entry_point_address;
  
    // Otherwise, look for the UnixThread or Thread command.  The data for the
    // Thread command is given in /usr/include/mach-o.h, but it is basically:
    //
    //  uint32_t flavor  - this is the flavor argument you would pass to
    //  thread_get_state
    //  uint32_t count   - this is the count of longs in the thread state data
    //  struct XXX_thread_state state - this is the structure from
    //  <machine/thread_status.h> corresponding to the flavor.
    //  <repeat this trio>
    //
    // So we just keep reading the various register flavors till we find the GPR
    // one, then read the PC out of there.
    // FIXME: We will need to have a "RegisterContext data provider" class at some
    // point that can get all the registers
    // out of data in this form & attach them to a given thread.  That should
    // underlie the MacOS X User process plugin, and we'll also need it for the
    // MacOS X Core File process plugin.  When we have that we can also use it
    // here.
    //
    // For now we hard-code the offsets and flavors we need:
    //
    //
  
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      struct load_command load_cmd;
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      uint32_t i;
      lldb::addr_t start_address = LLDB_INVALID_ADDRESS;
      bool done = false;
  
      for (i = 0; i < m_header.ncmds; ++i) {
        const lldb::offset_t cmd_offset = offset;
        if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
          break;
  
        switch (load_cmd.cmd) {
        case LC_UNIXTHREAD:
        case LC_THREAD: {
          while (offset < cmd_offset + load_cmd.cmdsize) {
            uint32_t flavor = m_data.GetU32(&offset);
            uint32_t count = m_data.GetU32(&offset);
            if (count == 0) {
              // We've gotten off somehow, log and exit;
              return m_entry_point_address;
            }
  
            switch (m_header.cputype) {
            case llvm::MachO::CPU_TYPE_ARM:
              if (flavor == 1 ||
                  flavor == 9) // ARM_THREAD_STATE/ARM_THREAD_STATE32 from
                               // mach/arm/thread_status.h
              {
                offset += 60; // This is the offset of pc in the GPR thread state
                              // data structure.
                start_address = m_data.GetU32(&offset);
                done = true;
              }
              break;
            case llvm::MachO::CPU_TYPE_ARM64:
              if (flavor == 6) // ARM_THREAD_STATE64 from mach/arm/thread_status.h
              {
                offset += 256; // This is the offset of pc in the GPR thread state
                               // data structure.
                start_address = m_data.GetU64(&offset);
                done = true;
              }
              break;
            case llvm::MachO::CPU_TYPE_I386:
              if (flavor ==
                  1) // x86_THREAD_STATE32 from mach/i386/thread_status.h
              {
                offset += 40; // This is the offset of eip in the GPR thread state
                              // data structure.
                start_address = m_data.GetU32(&offset);
                done = true;
              }
              break;
            case llvm::MachO::CPU_TYPE_X86_64:
              if (flavor ==
                  4) // x86_THREAD_STATE64 from mach/i386/thread_status.h
              {
                offset += 16 * 8; // This is the offset of rip in the GPR thread
                                  // state data structure.
                start_address = m_data.GetU64(&offset);
                done = true;
              }
              break;
            default:
              return m_entry_point_address;
            }
            // Haven't found the GPR flavor yet, skip over the data for this
            // flavor:
            if (done)
              break;
            offset += count * 4;
          }
        } break;
        case LC_MAIN: {
          ConstString text_segment_name("__TEXT");
          uint64_t entryoffset = m_data.GetU64(&offset);
          SectionSP text_segment_sp =
              GetSectionList()->FindSectionByName(text_segment_name);
          if (text_segment_sp) {
            done = true;
            start_address = text_segment_sp->GetFileAddress() + entryoffset;
          }
        } break;
  
        default:
          break;
        }
        if (done)
          break;
  
        // Go to the next load command:
        offset = cmd_offset + load_cmd.cmdsize;
      }
  
      if (start_address != LLDB_INVALID_ADDRESS) {
        // We got the start address from the load commands, so now resolve that
        // address in the sections of this ObjectFile:
        if (!m_entry_point_address.ResolveAddressUsingFileSections(
                start_address, GetSectionList())) {
          m_entry_point_address.Clear();
        }
      } else {
        // We couldn't read the UnixThread load command - maybe it wasn't there.
        // As a fallback look for the "start" symbol in the main executable.
  
        ModuleSP module_sp(GetModule());
  
        if (module_sp) {
          SymbolContextList contexts;
          SymbolContext context;
          if (module_sp->FindSymbolsWithNameAndType(ConstString("start"),
                                                    eSymbolTypeCode, contexts)) {
            if (contexts.GetContextAtIndex(0, context))
              m_entry_point_address = context.symbol->GetAddress();
          }
        }
      }
    }
  
    return m_entry_point_address;
  }
  
  lldb_private::Address ObjectFileMachO::GetBaseAddress() {
    lldb_private::Address header_addr;
    SectionList *section_list = GetSectionList();
    if (section_list) {
      SectionSP text_segment_sp(
          section_list->FindSectionByName(GetSegmentNameTEXT()));
      if (text_segment_sp) {
        header_addr.SetSection(text_segment_sp);
        header_addr.SetOffset(0);
      }
    }
    return header_addr;
  }
  
  uint32_t ObjectFileMachO::GetNumThreadContexts() {
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      if (!m_thread_context_offsets_valid) {
        m_thread_context_offsets_valid = true;
        lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
        FileRangeArray::Entry file_range;
        thread_command thread_cmd;
        for (uint32_t i = 0; i < m_header.ncmds; ++i) {
          const uint32_t cmd_offset = offset;
          if (m_data.GetU32(&offset, &thread_cmd, 2) == NULL)
            break;
  
          if (thread_cmd.cmd == LC_THREAD) {
            file_range.SetRangeBase(offset);
            file_range.SetByteSize(thread_cmd.cmdsize - 8);
            m_thread_context_offsets.Append(file_range);
          }
          offset = cmd_offset + thread_cmd.cmdsize;
        }
      }
    }
    return m_thread_context_offsets.GetSize();
  }
  
  std::string ObjectFileMachO::GetIdentifierString() {
    std::string result;
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
  
      // First, look over the load commands for an LC_NOTE load command with
      // data_owner string "kern ver str" & use that if found.
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      for (uint32_t i = 0; i < m_header.ncmds; ++i) {
        const uint32_t cmd_offset = offset;
        load_command lc;
        if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
            break;
        if (lc.cmd == LC_NOTE)
        {
            char data_owner[17];
            m_data.CopyData (offset, 16, data_owner);
            data_owner[16] = '\0';
            offset += 16;
            uint64_t fileoff = m_data.GetU64_unchecked (&offset);
            uint64_t size = m_data.GetU64_unchecked (&offset);
  
            // "kern ver str" has a uint32_t version and then a nul terminated
            // c-string.
            if (strcmp ("kern ver str", data_owner) == 0)
            {
                offset = fileoff;
                uint32_t version;
                if (m_data.GetU32 (&offset, &version, 1) != nullptr)
                {
                    if (version == 1)
                    {
                        uint32_t strsize = size - sizeof (uint32_t);
                        char *buf = (char*) malloc (strsize);
                        if (buf)
                        {
                            m_data.CopyData (offset, strsize, buf);
                            buf[strsize - 1] = '\0';
                            result = buf;
                            if (buf)
                                free (buf);
                            return result;
                        }
                    }
                }
            }
        }
        offset = cmd_offset + lc.cmdsize;
      }
  
      // Second, make a pass over the load commands looking for an obsolete
      // LC_IDENT load command.
      offset = MachHeaderSizeFromMagic(m_header.magic);
      for (uint32_t i = 0; i < m_header.ncmds; ++i) {
        const uint32_t cmd_offset = offset;
        struct ident_command ident_command;
        if (m_data.GetU32(&offset, &ident_command, 2) == NULL)
          break;
        if (ident_command.cmd == LC_IDENT && ident_command.cmdsize != 0) {
          char *buf = (char *) malloc (ident_command.cmdsize);
          if (buf != nullptr 
              && m_data.CopyData (offset, ident_command.cmdsize, buf) == ident_command.cmdsize) {
            buf[ident_command.cmdsize - 1] = '\0';
            result = buf;
          }
          if (buf)
            free (buf);
        }
        offset = cmd_offset + ident_command.cmdsize;
      }
  
    }
    return result;
  }
  
  bool ObjectFileMachO::GetCorefileMainBinaryInfo (addr_t &address, UUID &uuid) {
    address = LLDB_INVALID_ADDRESS;
    uuid.Clear();
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      for (uint32_t i = 0; i < m_header.ncmds; ++i) {
        const uint32_t cmd_offset = offset;
        load_command lc;
        if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
            break;
        if (lc.cmd == LC_NOTE)
        {
            char data_owner[17];
            memset (data_owner, 0, sizeof (data_owner));
            m_data.CopyData (offset, 16, data_owner);
            offset += 16;
            uint64_t fileoff = m_data.GetU64_unchecked (&offset);
            uint64_t size = m_data.GetU64_unchecked (&offset);
  
            // "main bin spec" (main binary specification) data payload is
            // formatted:
            //    uint32_t version       [currently 1]
            //    uint32_t type          [0 == unspecified, 1 == kernel, 2 == user process]
            //    uint64_t address       [ UINT64_MAX if address not specified ]
            //    uuid_t   uuid          [ all zero's if uuid not specified ]
            //    uint32_t log2_pagesize [ process page size in log base 2, e.g. 4k pages are 12.  0 for unspecified ]
  
            if (strcmp ("main bin spec", data_owner) == 0 && size >= 32)
            {
                offset = fileoff;
                uint32_t version;
                if (m_data.GetU32 (&offset, &version, 1) != nullptr && version == 1)
                {
                    uint32_t type = 0;
                    uuid_t raw_uuid;
                    memset (raw_uuid, 0, sizeof (uuid_t));
  
                    if (m_data.GetU32(&offset, &type, 1) &&
                        m_data.GetU64(&offset, &address, 1) &&
                        m_data.CopyData(offset, sizeof(uuid_t), raw_uuid) != 0) {
                      uuid = UUID::fromOptionalData(raw_uuid, sizeof(uuid_t));
                      return true;
                    }
                }
            }
        }
        offset = cmd_offset + lc.cmdsize;
      }
    }
    return false;
  }
  
  lldb::RegisterContextSP
  ObjectFileMachO::GetThreadContextAtIndex(uint32_t idx,
                                           lldb_private::Thread &thread) {
    lldb::RegisterContextSP reg_ctx_sp;
  
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      if (!m_thread_context_offsets_valid)
        GetNumThreadContexts();
  
      const FileRangeArray::Entry *thread_context_file_range =
          m_thread_context_offsets.GetEntryAtIndex(idx);
      if (thread_context_file_range) {
  
        DataExtractor data(m_data, thread_context_file_range->GetRangeBase(),
                           thread_context_file_range->GetByteSize());
  
        switch (m_header.cputype) {
        case llvm::MachO::CPU_TYPE_ARM64:
          reg_ctx_sp =
              std::make_shared<RegisterContextDarwin_arm64_Mach>(thread, data);
          break;
  
        case llvm::MachO::CPU_TYPE_ARM:
          reg_ctx_sp =
              std::make_shared<RegisterContextDarwin_arm_Mach>(thread, data);
          break;
  
        case llvm::MachO::CPU_TYPE_I386:
          reg_ctx_sp =
              std::make_shared<RegisterContextDarwin_i386_Mach>(thread, data);
          break;
  
        case llvm::MachO::CPU_TYPE_X86_64:
          reg_ctx_sp =
              std::make_shared<RegisterContextDarwin_x86_64_Mach>(thread, data);
          break;
        }
      }
    }
    return reg_ctx_sp;
  }
  
  ObjectFile::Type ObjectFileMachO::CalculateType() {
    switch (m_header.filetype) {
    case MH_OBJECT: // 0x1u
      if (GetAddressByteSize() == 4) {
        // 32 bit kexts are just object files, but they do have a valid
        // UUID load command.
        UUID uuid;
        if (GetUUID(&uuid)) {
          // this checking for the UUID load command is not enough we could
          // eventually look for the symbol named "OSKextGetCurrentIdentifier" as
          // this is required of kexts
          if (m_strata == eStrataInvalid)
            m_strata = eStrataKernel;
          return eTypeSharedLibrary;
        }
      }
      return eTypeObjectFile;
  
    case MH_EXECUTE:
      return eTypeExecutable; // 0x2u
    case MH_FVMLIB:
      return eTypeSharedLibrary; // 0x3u
    case MH_CORE:
      return eTypeCoreFile; // 0x4u
    case MH_PRELOAD:
      return eTypeSharedLibrary; // 0x5u
    case MH_DYLIB:
      return eTypeSharedLibrary; // 0x6u
    case MH_DYLINKER:
      return eTypeDynamicLinker; // 0x7u
    case MH_BUNDLE:
      return eTypeSharedLibrary; // 0x8u
    case MH_DYLIB_STUB:
      return eTypeStubLibrary; // 0x9u
    case MH_DSYM:
      return eTypeDebugInfo; // 0xAu
    case MH_KEXT_BUNDLE:
      return eTypeSharedLibrary; // 0xBu
    default:
      break;
    }
    return eTypeUnknown;
  }
  
  ObjectFile::Strata ObjectFileMachO::CalculateStrata() {
    switch (m_header.filetype) {
    case MH_OBJECT: // 0x1u
    {
      // 32 bit kexts are just object files, but they do have a valid
      // UUID load command.
      UUID uuid;
      if (GetUUID(&uuid)) {
        // this checking for the UUID load command is not enough we could
        // eventually look for the symbol named "OSKextGetCurrentIdentifier" as
        // this is required of kexts
        if (m_type == eTypeInvalid)
          m_type = eTypeSharedLibrary;
  
        return eStrataKernel;
      }
    }
      return eStrataUnknown;
  
    case MH_EXECUTE: // 0x2u
      // Check for the MH_DYLDLINK bit in the flags
      if (m_header.flags & MH_DYLDLINK) {
        return eStrataUser;
      } else {
        SectionList *section_list = GetSectionList();
        if (section_list) {
          static ConstString g_kld_section_name("__KLD");
          if (section_list->FindSectionByName(g_kld_section_name))
            return eStrataKernel;
        }
      }
      return eStrataRawImage;
  
    case MH_FVMLIB:
      return eStrataUser; // 0x3u
    case MH_CORE:
      return eStrataUnknown; // 0x4u
    case MH_PRELOAD:
      return eStrataRawImage; // 0x5u
    case MH_DYLIB:
      return eStrataUser; // 0x6u
    case MH_DYLINKER:
      return eStrataUser; // 0x7u
    case MH_BUNDLE:
      return eStrataUser; // 0x8u
    case MH_DYLIB_STUB:
      return eStrataUser; // 0x9u
    case MH_DSYM:
      return eStrataUnknown; // 0xAu
    case MH_KEXT_BUNDLE:
      return eStrataKernel; // 0xBu
    default:
      break;
    }
    return eStrataUnknown;
  }
  
  llvm::VersionTuple ObjectFileMachO::GetVersion() {
    ModuleSP module_sp(GetModule());
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
      struct dylib_command load_cmd;
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      uint32_t version_cmd = 0;
      uint64_t version = 0;
      uint32_t i;
      for (i = 0; i < m_header.ncmds; ++i) {
        const lldb::offset_t cmd_offset = offset;
        if (m_data.GetU32(&offset, &load_cmd, 2) == NULL)
          break;
  
        if (load_cmd.cmd == LC_ID_DYLIB) {
          if (version_cmd == 0) {
            version_cmd = load_cmd.cmd;
            if (m_data.GetU32(&offset, &load_cmd.dylib, 4) == NULL)
              break;
            version = load_cmd.dylib.current_version;
          }
          break; // Break for now unless there is another more complete version
                 // number load command in the future.
        }
        offset = cmd_offset + load_cmd.cmdsize;
      }
  
      if (version_cmd == LC_ID_DYLIB) {
        unsigned major = (version & 0xFFFF0000ull) >> 16;
        unsigned minor = (version & 0x0000FF00ull) >> 8;
        unsigned subminor = (version & 0x000000FFull);
        return llvm::VersionTuple(major, minor, subminor);
      }
    }
    return llvm::VersionTuple();
  }
  
  ArchSpec ObjectFileMachO::GetArchitecture() {
    ModuleSP module_sp(GetModule());
    ArchSpec arch;
    if (module_sp) {
      std::lock_guard<std::recursive_mutex> guard(module_sp->GetMutex());
  
      return GetArchitecture(m_header, m_data,
                             MachHeaderSizeFromMagic(m_header.magic));
    }
    return arch;
  }
  
  void ObjectFileMachO::GetProcessSharedCacheUUID(Process *process, addr_t &base_addr, UUID &uuid) {
    uuid.Clear();
    base_addr = LLDB_INVALID_ADDRESS;
    if (process && process->GetDynamicLoader()) {
      DynamicLoader *dl = process->GetDynamicLoader();
      LazyBool using_shared_cache;
      LazyBool private_shared_cache;
      dl->GetSharedCacheInformation(base_addr, uuid, using_shared_cache,
                                    private_shared_cache);
    }
    Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS | LIBLLDB_LOG_PROCESS));
    if (log)
      log->Printf("inferior process shared cache has a UUID of %s, base address 0x%" PRIx64 , uuid.GetAsString().c_str(), base_addr);
  }
  
  // From dyld SPI header dyld_process_info.h
  typedef void *dyld_process_info;
  struct lldb_copy__dyld_process_cache_info {
    uuid_t cacheUUID;          // UUID of cache used by process
    uint64_t cacheBaseAddress; // load address of dyld shared cache
    bool noCache;              // process is running without a dyld cache
    bool privateCache; // process is using a private copy of its dyld cache
  };
  
  // #including mach/mach.h pulls in machine.h & CPU_TYPE_ARM etc conflicts with llvm
  // enum definitions llvm::MachO::CPU_TYPE_ARM turning them into compile errors.
  // So we need to use the actual underlying types of task_t and kern_return_t
  // below.
  extern "C" unsigned int /*task_t*/ mach_task_self(); 
  
  void ObjectFileMachO::GetLLDBSharedCacheUUID(addr_t &base_addr, UUID &uuid) {
    uuid.Clear();
    base_addr = LLDB_INVALID_ADDRESS;
  
  #if defined(__APPLE__) &&                                                      \
      (defined(__arm__) || defined(__arm64__) || defined(__aarch64__))
    uint8_t *(*dyld_get_all_image_infos)(void);
    dyld_get_all_image_infos =
        (uint8_t * (*)())dlsym(RTLD_DEFAULT, "_dyld_get_all_image_infos");
    if (dyld_get_all_image_infos) {
      uint8_t *dyld_all_image_infos_address = dyld_get_all_image_infos();
      if (dyld_all_image_infos_address) {
        uint32_t *version = (uint32_t *)
            dyld_all_image_infos_address; // version <mach-o/dyld_images.h>
        if (*version >= 13) {
          uuid_t *sharedCacheUUID_address = 0;
          int wordsize = sizeof(uint8_t *);
          if (wordsize == 8) {
            sharedCacheUUID_address =
                (uuid_t *)((uint8_t *)dyld_all_image_infos_address +
                           160); // sharedCacheUUID <mach-o/dyld_images.h>
            if (*version >= 15)
              base_addr = *(uint64_t *) ((uint8_t *) dyld_all_image_infos_address 
                            + 176); // sharedCacheBaseAddress <mach-o/dyld_images.h>
          } else {
            sharedCacheUUID_address =
                (uuid_t *)((uint8_t *)dyld_all_image_infos_address +
                           84); // sharedCacheUUID <mach-o/dyld_images.h>
            if (*version >= 15) {
              base_addr = 0;
              base_addr = *(uint32_t *) ((uint8_t *) dyld_all_image_infos_address 
                            + 100); // sharedCacheBaseAddress <mach-o/dyld_images.h>
            }
          }
          uuid = UUID::fromOptionalData(sharedCacheUUID_address, sizeof(uuid_t));
        }
      }
    } else {
      // Exists in macOS 10.12 and later, iOS 10.0 and later - dyld SPI
      dyld_process_info (*dyld_process_info_create)(unsigned int /* task_t */ task, uint64_t timestamp, unsigned int /*kern_return_t*/ *kernelError);
      void (*dyld_process_info_get_cache)(void *info, void *cacheInfo);
      void (*dyld_process_info_release)(dyld_process_info info);
  
      dyld_process_info_create = (void *(*)(unsigned int /* task_t */, uint64_t, unsigned int /*kern_return_t*/ *))
                 dlsym (RTLD_DEFAULT, "_dyld_process_info_create");
      dyld_process_info_get_cache = (void (*)(void *, void *))
                 dlsym (RTLD_DEFAULT, "_dyld_process_info_get_cache");
      dyld_process_info_release = (void (*)(void *))
                 dlsym (RTLD_DEFAULT, "_dyld_process_info_release");
  
      if (dyld_process_info_create && dyld_process_info_get_cache) {
        unsigned int /*kern_return_t */ kern_ret;
  		  dyld_process_info process_info = dyld_process_info_create(::mach_task_self(), 0, &kern_ret);
        if (process_info) {
          struct lldb_copy__dyld_process_cache_info sc_info;
          memset (&sc_info, 0, sizeof (struct lldb_copy__dyld_process_cache_info));
          dyld_process_info_get_cache (process_info, &sc_info);
          if (sc_info.cacheBaseAddress != 0) {
            base_addr = sc_info.cacheBaseAddress;
            uuid = UUID::fromOptionalData(sc_info.cacheUUID, sizeof(uuid_t));
          }
          dyld_process_info_release (process_info);
        }
      }
    }
    Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_SYMBOLS | LIBLLDB_LOG_PROCESS));
    if (log && uuid.IsValid())
      log->Printf("lldb's in-memory shared cache has a UUID of %s base address of 0x%" PRIx64, uuid.GetAsString().c_str(), base_addr);
  #endif
  }
  
  llvm::VersionTuple ObjectFileMachO::GetMinimumOSVersion() {
    if (!m_min_os_version) {
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      for (uint32_t i = 0; i < m_header.ncmds; ++i) {
        const lldb::offset_t load_cmd_offset = offset;
  
        version_min_command lc;
        if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
          break;
        if (lc.cmd == llvm::MachO::LC_VERSION_MIN_MACOSX ||
            lc.cmd == llvm::MachO::LC_VERSION_MIN_IPHONEOS ||
            lc.cmd == llvm::MachO::LC_VERSION_MIN_TVOS ||
            lc.cmd == llvm::MachO::LC_VERSION_MIN_WATCHOS) {
          if (m_data.GetU32(&offset, &lc.version,
                            (sizeof(lc) / sizeof(uint32_t)) - 2)) {
            const uint32_t xxxx = lc.version >> 16;
            const uint32_t yy = (lc.version >> 8) & 0xffu;
            const uint32_t zz = lc.version & 0xffu;
            if (xxxx) {
              m_min_os_version = llvm::VersionTuple(xxxx, yy, zz);
              break;
            }
          } 
        } else if (lc.cmd == llvm::MachO::LC_BUILD_VERSION) {
          // struct build_version_command {
          //     uint32_t    cmd;            /* LC_BUILD_VERSION */
          //     uint32_t    cmdsize;        /* sizeof(struct build_version_command) plus */
          //                                 /* ntools * sizeof(struct build_tool_version) */
          //     uint32_t    platform;       /* platform */
          //     uint32_t    minos;          /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
          //     uint32_t    sdk;            /* X.Y.Z is encoded in nibbles xxxx.yy.zz */
          //     uint32_t    ntools;         /* number of tool entries following this */
          // };
  
          offset += 4;  // skip platform
          uint32_t minos = m_data.GetU32(&offset);
  
          const uint32_t xxxx = minos >> 16;
          const uint32_t yy = (minos >> 8) & 0xffu;
          const uint32_t zz = minos & 0xffu;
          if (xxxx) {
              m_min_os_version = llvm::VersionTuple(xxxx, yy, zz);
              break;
          }
        }
  
        offset = load_cmd_offset + lc.cmdsize;
      }
  
      if (!m_min_os_version) {
        // Set version to an empty value so we don't keep trying to
        m_min_os_version = llvm::VersionTuple();
      }
    }
  
    return *m_min_os_version;
  }
  
  uint32_t ObjectFileMachO::GetSDKVersion(uint32_t *versions,
                                          uint32_t num_versions) {
    if (m_sdk_versions.empty()) {
      lldb::offset_t offset = MachHeaderSizeFromMagic(m_header.magic);
      bool success = false;
      for (uint32_t i = 0; !success && i < m_header.ncmds; ++i) {
        const lldb::offset_t load_cmd_offset = offset;
  
        version_min_command lc;
        if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
          break;
        if (lc.cmd == llvm::MachO::LC_VERSION_MIN_MACOSX ||
            lc.cmd == llvm::MachO::LC_VERSION_MIN_IPHONEOS ||
            lc.cmd == llvm::MachO::LC_VERSION_MIN_TVOS ||
            lc.cmd == llvm::MachO::LC_VERSION_MIN_WATCHOS) {
          if (m_data.GetU32(&offset, &lc.version,
                            (sizeof(lc) / sizeof(uint32_t)) - 2)) {
            const uint32_t xxxx = lc.sdk >> 16;
            const uint32_t yy = (lc.sdk >> 8) & 0xffu;
            const uint32_t zz = lc.sdk & 0xffu;
            if (xxxx) {
              m_sdk_versions.push_back(xxxx);
              m_sdk_versions.push_back(yy);
              m_sdk_versions.push_back(zz);
              success = true;
            } else {
              GetModule()->ReportWarning(
                  "minimum OS version load command with invalid (0) version found.");
            }
          }
        }
        offset = load_cmd_offset + lc.cmdsize;
      }
  
      if (!success) {
        offset = MachHeaderSizeFromMagic(m_header.magic);
        for (uint32_t i = 0; !success && i < m_header.ncmds; ++i) {
          const lldb::offset_t load_cmd_offset = offset;
  
          version_min_command lc;
          if (m_data.GetU32(&offset, &lc.cmd, 2) == NULL)
            break;
          if (lc.cmd == llvm::MachO::LC_BUILD_VERSION) {
            // struct build_version_command {
            //     uint32_t    cmd;            /* LC_BUILD_VERSION */
            //     uint32_t    cmdsize;        /* sizeof(struct
            //     build_version_command) plus */
            //                                 /* ntools * sizeof(struct
            //                                 build_tool_version) */
            //     uint32_t    platform;       /* platform */
            //     uint32_t    minos;          /* X.Y.Z is encoded in nibbles
            //     xxxx.yy.zz */ uint32_t    sdk;            /* X.Y.Z is encoded
            //     in nibbles xxxx.yy.zz */ uint32_t    ntools;         /* number
            //     of tool entries following this */
            // };
  
            offset += 4; // skip platform
            uint32_t minos = m_data.GetU32(&offset);
  
            const uint32_t xxxx = minos >> 16;
            const uint32_t yy = (minos >> 8) & 0xffu;
            const uint32_t zz = minos & 0xffu;
            if (xxxx) {
              m_sdk_versions.push_back(xxxx);
              m_sdk_versions.push_back(yy);
              m_sdk_versions.push_back(zz);
              success = true;
            }
          }
          offset = load_cmd_offset + lc.cmdsize;
        }
      }
  
      if (!success) {
        // Push an invalid value so we don't try to find
        // the version # again on the next call to this
        // method.
        m_sdk_versions.push_back(UINT32_MAX);
      }
    }
  
    // Legitimate version numbers will have 3 entries pushed
    // on to m_sdk_versions.  If we only have one value, it's
    // the sentinel value indicating that this object file
    // does not have a valid minimum os version #.
    if (m_sdk_versions.size() > 1) {
      if (versions != NULL && num_versions > 0) {
        for (size_t i = 0; i < num_versions; ++i) {
          if (i < m_sdk_versions.size())
            versions[i] = m_sdk_versions[i];
          else
            versions[i] = 0;
        }
      }
      return m_sdk_versions.size();
    }
    // Call the superclasses version that will empty out the data
    return ObjectFile::GetSDKVersion(versions, num_versions);
  }
  
  bool ObjectFileMachO::GetIsDynamicLinkEditor() {
    return m_header.filetype == llvm::MachO::MH_DYLINKER;
  }
  
  bool ObjectFileMachO::AllowAssemblyEmulationUnwindPlans() {
    return m_allow_assembly_emulation_unwind_plans;
  }
  
  //------------------------------------------------------------------
  // PluginInterface protocol
  //------------------------------------------------------------------
  lldb_private::ConstString ObjectFileMachO::GetPluginName() {
    return GetPluginNameStatic();
  }
  
  uint32_t ObjectFileMachO::GetPluginVersion() { return 1; }
  
  Section *ObjectFileMachO::GetMachHeaderSection() {
    // Find the first address of the mach header which is the first non-zero file
    // sized section whose file offset is zero. This is the base file address of
    // the mach-o file which can be subtracted from the vmaddr of the other
    // segments found in memory and added to the load address
    ModuleSP module_sp = GetModule();
    if (!module_sp)
      return nullptr;
    SectionList *section_list = GetSectionList();
    if (!section_list)
      return nullptr;
    const size_t num_sections = section_list->GetSize();
    for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
      Section *section = section_list->GetSectionAtIndex(sect_idx).get();
      if (section->GetFileOffset() == 0 && SectionIsLoadable(section))
        return section;
    }
    return nullptr;
  }
  
  bool ObjectFileMachO::SectionIsLoadable(const Section *section) {
    if (!section)
      return false;
    const bool is_dsym = (m_header.filetype == MH_DSYM);
    if (section->GetFileSize() == 0 && !is_dsym)
      return false;
    if (section->IsThreadSpecific())
      return false;
    if (GetModule().get() != section->GetModule().get())
      return false;
    // Be careful with __LINKEDIT and __DWARF segments
    if (section->GetName() == GetSegmentNameLINKEDIT() ||
        section->GetName() == GetSegmentNameDWARF()) {
      // Only map __LINKEDIT and __DWARF if we have an in memory image and
      // this isn't a kernel binary like a kext or mach_kernel.
      const bool is_memory_image = (bool)m_process_wp.lock();
      const Strata strata = GetStrata();
      if (is_memory_image == false || strata == eStrataKernel)
        return false;
    }
    return true;
  }
  
  lldb::addr_t ObjectFileMachO::CalculateSectionLoadAddressForMemoryImage(
      lldb::addr_t header_load_address, const Section *header_section,
      const Section *section) {
    ModuleSP module_sp = GetModule();
    if (module_sp && header_section && section &&
        header_load_address != LLDB_INVALID_ADDRESS) {
      lldb::addr_t file_addr = header_section->GetFileAddress();
      if (file_addr != LLDB_INVALID_ADDRESS && SectionIsLoadable(section))
        return section->GetFileAddress() - file_addr + header_load_address;
    }
    return LLDB_INVALID_ADDRESS;
  }
  
  bool ObjectFileMachO::SetLoadAddress(Target &target, lldb::addr_t value,
                                       bool value_is_offset) {
    ModuleSP module_sp = GetModule();
    if (module_sp) {
      size_t num_loaded_sections = 0;
      SectionList *section_list = GetSectionList();
      if (section_list) {
        const size_t num_sections = section_list->GetSize();
  
        if (value_is_offset) {
          // "value" is an offset to apply to each top level segment
          for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
            // Iterate through the object file sections to find all of the
            // sections that size on disk (to avoid __PAGEZERO) and load them
            SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
            if (SectionIsLoadable(section_sp.get()))
              if (target.GetSectionLoadList().SetSectionLoadAddress(
                      section_sp, section_sp->GetFileAddress() + value))
                ++num_loaded_sections;
          }
        } else {
          // "value" is the new base address of the mach_header, adjust each
          // section accordingly
  
          Section *mach_header_section = GetMachHeaderSection();
          if (mach_header_section) {
            for (size_t sect_idx = 0; sect_idx < num_sections; ++sect_idx) {
              SectionSP section_sp(section_list->GetSectionAtIndex(sect_idx));
  
              lldb::addr_t section_load_addr =
                  CalculateSectionLoadAddressForMemoryImage(
                      value, mach_header_section, section_sp.get());
              if (section_load_addr != LLDB_INVALID_ADDRESS) {
                if (target.GetSectionLoadList().SetSectionLoadAddress(
                        section_sp, section_load_addr))
                  ++num_loaded_sections;
              }
            }
          }
        }
      }
      return num_loaded_sections > 0;
    }
    return false;
  }
  
  bool ObjectFileMachO::SaveCore(const lldb::ProcessSP &process_sp,
                                 const FileSpec &outfile, Status &error) {
    if (process_sp) {
      Target &target = process_sp->GetTarget();
      const ArchSpec target_arch = target.GetArchitecture();
      const llvm::Triple &target_triple = target_arch.GetTriple();
      if (target_triple.getVendor() == llvm::Triple::Apple &&
          (target_triple.getOS() == llvm::Triple::MacOSX ||
           target_triple.getOS() == llvm::Triple::IOS ||
           target_triple.getOS() == llvm::Triple::WatchOS ||
           target_triple.getOS() == llvm::Triple::TvOS)) {
           // NEED_BRIDGEOS_TRIPLE target_triple.getOS() == llvm::Triple::BridgeOS)) {
        bool make_core = false;
        switch (target_arch.GetMachine()) {
        case llvm::Triple::aarch64:
        case llvm::Triple::arm:
        case llvm::Triple::thumb:
        case llvm::Triple::x86:
        case llvm::Triple::x86_64:
          make_core = true;
          break;
        default:
          error.SetErrorStringWithFormat("unsupported core architecture: %s",
                                         target_triple.str().c_str());
          break;
        }
  
        if (make_core) {
          std::vector<segment_command_64> segment_load_commands;
          //                uint32_t range_info_idx = 0;
          MemoryRegionInfo range_info;
          Status range_error = process_sp->GetMemoryRegionInfo(0, range_info);
          const uint32_t addr_byte_size = target_arch.GetAddressByteSize();
          const ByteOrder byte_order = target_arch.GetByteOrder();
          if (range_error.Success()) {
            while (range_info.GetRange().GetRangeBase() != LLDB_INVALID_ADDRESS) {
              const addr_t addr = range_info.GetRange().GetRangeBase();
              const addr_t size = range_info.GetRange().GetByteSize();
  
              if (size == 0)
                break;
  
              // Calculate correct protections
              uint32_t prot = 0;
              if (range_info.GetReadable() == MemoryRegionInfo::eYes)
                prot |= VM_PROT_READ;
              if (range_info.GetWritable() == MemoryRegionInfo::eYes)
                prot |= VM_PROT_WRITE;
              if (range_info.GetExecutable() == MemoryRegionInfo::eYes)
                prot |= VM_PROT_EXECUTE;
  
              if (prot != 0) {
                uint32_t cmd_type = LC_SEGMENT_64;
                uint32_t segment_size = sizeof(segment_command_64);
                if (addr_byte_size == 4) {
                  cmd_type = LC_SEGMENT;
                  segment_size = sizeof(segment_command);
                }
                segment_command_64 segment = {
                    cmd_type,     // uint32_t cmd;
                    segment_size, // uint32_t cmdsize;
                    {0},          // char segname[16];
                    addr, // uint64_t vmaddr;    // uint32_t for 32-bit Mach-O
                    size, // uint64_t vmsize;    // uint32_t for 32-bit Mach-O
                    0,    // uint64_t fileoff;   // uint32_t for 32-bit Mach-O
                    size, // uint64_t filesize;  // uint32_t for 32-bit Mach-O
                    prot, // uint32_t maxprot;
                    prot, // uint32_t initprot;
                    0,    // uint32_t nsects;
                    0};   // uint32_t flags;
                segment_load_commands.push_back(segment);
              } else {
                // No protections and a size of 1 used to be returned from old
                // debugservers when we asked about a region that was past the
                // last memory region and it indicates the end...
                if (size == 1)
                  break;
              }
  
              range_error = process_sp->GetMemoryRegionInfo(
                  range_info.GetRange().GetRangeEnd(), range_info);
              if (range_error.Fail())
                break;
            }
  
            StreamString buffer(Stream::eBinary, addr_byte_size, byte_order);
  
            mach_header_64 mach_header;
            if (addr_byte_size == 8) {
              mach_header.magic = MH_MAGIC_64;
            } else {
              mach_header.magic = MH_MAGIC;
            }
            mach_header.cputype = target_arch.GetMachOCPUType();
            mach_header.cpusubtype = target_arch.GetMachOCPUSubType();
            mach_header.filetype = MH_CORE;
            mach_header.ncmds = segment_load_commands.size();
            mach_header.flags = 0;
            mach_header.reserved = 0;
            ThreadList &thread_list = process_sp->GetThreadList();
            const uint32_t num_threads = thread_list.GetSize();
  
            // Make an array of LC_THREAD data items. Each one contains the
            // contents of the LC_THREAD load command. The data doesn't contain
            // the load command + load command size, we will add the load command
            // and load command size as we emit the data.
            std::vector<StreamString> LC_THREAD_datas(num_threads);
            for (auto &LC_THREAD_data : LC_THREAD_datas) {
              LC_THREAD_data.GetFlags().Set(Stream::eBinary);
              LC_THREAD_data.SetAddressByteSize(addr_byte_size);
              LC_THREAD_data.SetByteOrder(byte_order);
            }
            for (uint32_t thread_idx = 0; thread_idx < num_threads;
                 ++thread_idx) {
              ThreadSP thread_sp(thread_list.GetThreadAtIndex(thread_idx));
              if (thread_sp) {
                switch (mach_header.cputype) {
                case llvm::MachO::CPU_TYPE_ARM64:
                  RegisterContextDarwin_arm64_Mach::Create_LC_THREAD(
                      thread_sp.get(), LC_THREAD_datas[thread_idx]);
                  break;
  
                case llvm::MachO::CPU_TYPE_ARM:
                  RegisterContextDarwin_arm_Mach::Create_LC_THREAD(
                      thread_sp.get(), LC_THREAD_datas[thread_idx]);
                  break;
  
                case llvm::MachO::CPU_TYPE_I386:
                  RegisterContextDarwin_i386_Mach::Create_LC_THREAD(
                      thread_sp.get(), LC_THREAD_datas[thread_idx]);
                  break;
  
                case llvm::MachO::CPU_TYPE_X86_64:
                  RegisterContextDarwin_x86_64_Mach::Create_LC_THREAD(
                      thread_sp.get(), LC_THREAD_datas[thread_idx]);
                  break;
                }
              }
            }
  
            // The size of the load command is the size of the segments...
            if (addr_byte_size == 8) {
              mach_header.sizeofcmds = segment_load_commands.size() *
                                       sizeof(struct segment_command_64);
            } else {
              mach_header.sizeofcmds =
                  segment_load_commands.size() * sizeof(struct segment_command);
            }
  
            // and the size of all LC_THREAD load command
            for (const auto &LC_THREAD_data : LC_THREAD_datas) {
              ++mach_header.ncmds;
              mach_header.sizeofcmds += 8 + LC_THREAD_data.GetSize();
            }
  
            printf("mach_header: 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x "
                   "0x%8.8x 0x%8.8x\n",
                   mach_header.magic, mach_header.cputype, mach_header.cpusubtype,
                   mach_header.filetype, mach_header.ncmds,
                   mach_header.sizeofcmds, mach_header.flags,
                   mach_header.reserved);
  
            // Write the mach header
            buffer.PutHex32(mach_header.magic);
            buffer.PutHex32(mach_header.cputype);
            buffer.PutHex32(mach_header.cpusubtype);
            buffer.PutHex32(mach_header.filetype);
            buffer.PutHex32(mach_header.ncmds);
            buffer.PutHex32(mach_header.sizeofcmds);
            buffer.PutHex32(mach_header.flags);
            if (addr_byte_size == 8) {
              buffer.PutHex32(mach_header.reserved);
            }
  
            // Skip the mach header and all load commands and align to the next
            // 0x1000 byte boundary
            addr_t file_offset = buffer.GetSize() + mach_header.sizeofcmds;
            if (file_offset & 0x00000fff) {
              file_offset += 0x00001000ull;
              file_offset &= (~0x00001000ull + 1);
            }
  
            for (auto &segment : segment_load_commands) {
              segment.fileoff = file_offset;
              file_offset += segment.filesize;
            }
  
            // Write out all of the LC_THREAD load commands
            for (const auto &LC_THREAD_data : LC_THREAD_datas) {
              const size_t LC_THREAD_data_size = LC_THREAD_data.GetSize();
              buffer.PutHex32(LC_THREAD);
              buffer.PutHex32(8 + LC_THREAD_data_size); // cmd + cmdsize + data
              buffer.Write(LC_THREAD_data.GetString().data(),
                           LC_THREAD_data_size);
            }
  
            // Write out all of the segment load commands
            for (const auto &segment : segment_load_commands) {
              printf("0x%8.8x 0x%8.8x [0x%16.16" PRIx64 " - 0x%16.16" PRIx64
                     ") [0x%16.16" PRIx64 " 0x%16.16" PRIx64
                     ") 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x]\n",
                     segment.cmd, segment.cmdsize, segment.vmaddr,
                     segment.vmaddr + segment.vmsize, segment.fileoff,
                     segment.filesize, segment.maxprot, segment.initprot,
                     segment.nsects, segment.flags);
  
              buffer.PutHex32(segment.cmd);
              buffer.PutHex32(segment.cmdsize);
              buffer.PutRawBytes(segment.segname, sizeof(segment.segname));
              if (addr_byte_size == 8) {
                buffer.PutHex64(segment.vmaddr);
                buffer.PutHex64(segment.vmsize);
                buffer.PutHex64(segment.fileoff);
                buffer.PutHex64(segment.filesize);
              } else {
                buffer.PutHex32(static_cast<uint32_t>(segment.vmaddr));
                buffer.PutHex32(static_cast<uint32_t>(segment.vmsize));
                buffer.PutHex32(static_cast<uint32_t>(segment.fileoff));
                buffer.PutHex32(static_cast<uint32_t>(segment.filesize));
              }
              buffer.PutHex32(segment.maxprot);
              buffer.PutHex32(segment.initprot);
              buffer.PutHex32(segment.nsects);
              buffer.PutHex32(segment.flags);
            }
  
            File core_file;
            std::string core_file_path(outfile.GetPath());
            error = FileSystem::Instance().Open(core_file, outfile,
                                                File::eOpenOptionWrite |
                                                    File::eOpenOptionTruncate |
                                                    File::eOpenOptionCanCreate);
            if (error.Success()) {
              // Read 1 page at a time
              uint8_t bytes[0x1000];
              // Write the mach header and load commands out to the core file
              size_t bytes_written = buffer.GetString().size();
              error = core_file.Write(buffer.GetString().data(), bytes_written);
              if (error.Success()) {
                // Now write the file data for all memory segments in the process
                for (const auto &segment : segment_load_commands) {
                  if (core_file.SeekFromStart(segment.fileoff) == -1) {
                    error.SetErrorStringWithFormat(
                        "unable to seek to offset 0x%" PRIx64 " in '%s'",
                        segment.fileoff, core_file_path.c_str());
                    break;
                  }
  
                  printf("Saving %" PRId64
                         " bytes of data for memory region at 0x%" PRIx64 "\n",
                         segment.vmsize, segment.vmaddr);
                  addr_t bytes_left = segment.vmsize;
                  addr_t addr = segment.vmaddr;
                  Status memory_read_error;
                  while (bytes_left > 0 && error.Success()) {
                    const size_t bytes_to_read =
                        bytes_left > sizeof(bytes) ? sizeof(bytes) : bytes_left;
  
                    // In a savecore setting, we don't really care about caching,
                    // as the data is dumped and very likely never read again,
                    // so we call ReadMemoryFromInferior to bypass it.
                    const size_t bytes_read = process_sp->ReadMemoryFromInferior(
                        addr, bytes, bytes_to_read, memory_read_error);
  
                    if (bytes_read == bytes_to_read) {
                      size_t bytes_written = bytes_read;
                      error = core_file.Write(bytes, bytes_written);
                      bytes_left -= bytes_read;
                      addr += bytes_read;
                    } else {
                      // Some pages within regions are not readable, those should
                      // be zero filled
                      memset(bytes, 0, bytes_to_read);
                      size_t bytes_written = bytes_to_read;
                      error = core_file.Write(bytes, bytes_written);
                      bytes_left -= bytes_to_read;
                      addr += bytes_to_read;
                    }
                  }
                }
              }
            }
          } else {
            error.SetErrorString(
                "process doesn't support getting memory region info");
          }
        }
        return true; // This is the right plug to handle saving core files for
                     // this process
      }
    }
    return false;
  }
  