首页
社区
课程
招聘
[原创]ptrace学习: 阅读 lldb 相关源码
发表于: 2025-10-12 09:22 1045

[原创]ptrace学习: 阅读 lldb 相关源码

2025-10-12 09:22
1045

阅读 lldb 源码学习 ptrace 的使用.

来到 lldb/source/Plugins/Process/Linux 目录中.

先看头文件, 了解下实现了那些功能:

发现 NativeProcessLinux 类的构造方法是一个私有方法, 找了一下发现 NativeProcessLinux::Manager 中的 Launch Attach 才是入口, 先看一下 Launch:

整体浏览下来逻辑如下:

然我们仔细看一下实现, 首先是 LaunchProcess, 看看如何创建子进程, 创建后都做了什么:

通过 fork 创建子进程, 并将子进程的启动参数 launch_info 包装为 ForkLaunchInfo 类的实例 fork_launch_info, 再跟入 ChildFunc 函数:

针对启动参数做了很多处理, 其实关键的只有两步:

让我们回来再看接着看 NativeProcessLinux::Manager::Launch 方法, 跟入 SetDefaultPtraceOpts:

SetDefaultPtraceOpts 方法用于设置 ptrace 的选项, 再由 PtraceWrapper 方法调用 ptrace, 并且 PtraceWrapper 方法还封装了一些日志, 错误处理的逻辑.

在回到 NativeProcessLinux::Manager::Launch 跟入 NativeRegisterContextLinux::DetermineArchitecture, 查看获取错误信息的实现, 这里进入了 NativeRegisterContextLinux*.cpp 系列文件, NativeRegisterContextLinux.cppNativeRegisterContextLinux.h 提供统一接口, 其他文件对应不同架构的实例化.

这个跟了一下流程比较长, 并且与咱们的要探究的 ptrace 使用没什么太大的关系, 不再赘述, 感兴趣可以自己看一下.

再来看一下 NativeProcessLinux::Manager::Attach:

附加一个进程要做的是就是根据这个进程 pid 找到它所有的线程, 然后 PtraceWrapper(PTRACE_ATTACH, tid) 用 ptrace 向所有的线程发送 PTRACE_ATTACH 命令​​, 此时这个进程会进入被调试状态, 内核向被调试进程中的每个线程发送一个 SIGSTOP 信号使其暂停, 处于 PTRACED 状态, 而调用 ptrace 的进程, 即调试进程等待 SIGCHLD 信号.

再看一下 FindProcessThreads 方法:

相当于这种:

读取 /proc/[pid]/task/ 虚拟文件中的信息, 找到对应 pid 的 tid.

通过向被调试进程发送 SIGSTOP 信号来强制进程暂停, 是调试过程中中断程序运行的常用手段. 调试器后续可以通过 Resume 方法恢复进程执行.

恢复被调试进程中线程的执行, 根据 resume_actions 中指定的动作控制每个线程的行为, 如继续运行、单步执行等. 其中有几个值得细看的方法, 让我们一一探究一下.

首先是 SupportHardwareSingleStepping:

根据机构类型确定是否支持硬件断点, 硬件断点需要硬件支持(这个像是废话), 后面会看如何实现硬件/软件断点. 这个架构类型信息 m_arch 是在 Launch / Attach 中确定的, 具体来说是在编译时通过 cmake 确定的. 可以看一下 llvm 支持的所有架构:

再来看一下 SetupSoftwareSingleStepping:

设置软件单步断点, 实现逻辑主要为:

我们按顺序看一下 ReadMemoryCallback, ReadRegisterCallback(WriteMemoryCallback WriteRegisterCallback 不看, 几乎没有内容):

emulator_baton->m_process.ReadMemory 对应了 NativeProcessLinux 类中的 ReadMemory 方法:

首先尝试用 process_vm_readv 系统调用, 如果不支持, 使用 ptrace 的包装方法 PtraceWrapper 发送 PTRACE_PEEKDATA 命令.

我们可以再看一下 NativeProcessLinux 类中的 ReadMemory 方法:

可以看到还是依赖的 ptrace 的包装方法 PtraceWrapper 发送 PTRACE_POKEDATA 命令.

emulator_baton->m_reg_context.ReadRegister 对应了 NativeRegisterContext 类中的 ReadRegister 虚方法, 对于不同的架构有不同的实现, 这里我们看 lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp:

ReadGPR ReadFPR ReadAllSVE...实现上都差不多:

都是调用 ReadRegisterSet:

可以看到是调用 ptrace 的包装方法 PtraceWrapper 发送 PTRACE_GETREGSET 命令. 同样的在 lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux.cpp 目录中有这样一些列方法:

可以预料的是, 写入寄存器也是依赖 PtraceWrapper 发送命令.

总结来看 读/写 -> 内存/寄存器, 本质上都是依赖 ptrace 提供的命令, 然后做一些错误, 日志, 拷贝...处理.

回到 SetupSoftwareSingleStepping 跟入 emulator_up->EvaluateInstruction:

根据不同的 opcode 调用对应的指令处理方法, 在软件层面解析并模拟执行指令, 而不依赖实际的硬件处理器执行, 有一个很长的对应表:

然后得到计算出新 PC 值. 这个模拟解释执行指令这里涉及的东西比较多, 并且与 ptrace 关系不大, 这个不在详细看了, 感兴趣可以自己根根看.

在回到 SetupSoftwareSingleStepping 跟入 process.SetBreakpoint, 实际是 NativeProcessLinux 类的 SetBreakpoint 方法:

封装了 NativeProcessLinuxSetHardwareBreakpointSetSoftwareBreakpoint 方法, 我们一一分析一下:

跟入 EnableSoftwareBreakpoint:

这里把源代码的提示字符串也写成的中文, 函数执行逻辑如下:

thread->SetHardwareBreakpoint 对应了 NativeThreadLinux 中的 SetHardwareBreakpoint:

跟入 m_reg_context_up->SetHardwareBreakpoint, 我们还是看 arm64 的实现, 在 lldb/source/Plugins/Process/Utility/NativeRegisterContextDBReg_arm64.cpp 文件中:

这个 WriteHardwareDebugRegs 实现在 lldb/source/Plugins/Process/Linux/NativeRegisterContextLinux_arm64.cpp 中:

实际上是在通过 ptrace 设置 debug 寄存器, 并且做好记录和检测.

最后让我们再回到 Resume 方法, 跟入 ResumeThread:

先来看看 thread.Resume, 对应了 NativeThreadLinux 类的 Resume 方法:

刷新所有与硬件有关的断点, 然后通过 PtraceWrapper 发送 ptracePTRACE_CONT 命令, 恢复进程运行. (SetWatchpointSetHardwareBreakpoint 类似, 不再赘述)

再来看看 thread.SingleStep, 对应了 NativeThreadLinux 类的 SingleStep 方法:

硬件单步对应 PTRACE_SINGLESTEP 命令, 软件单步对应 PTRACE_CONT 命令(下一条已在 NativeProcessLinux::Resume 中设被替换为断点指令).

至此我们都旅程结束.

/usr/include/x86_64-linux-gnu/bits/signum-arch.h

一定程度上反映了 ptrace 的功能:

namespace lldb_private {
class Status;
class Scalar;
 
namespace process_linux {
/// \class NativeProcessLinux
/// 管理与被调试进程(inferior)的通信
///
/// 构造时, 该类准备并启动一个用于调试的被调试进程
///
/// 被调试进程状态的变化会被广播出去
class NativeProcessLinux : public NativeProcessELF,
                           private NativeProcessSoftwareSingleStep {
public:
  class Manager : public NativeProcessProtocol::Manager {
  public:
    Manager(MainLoop &mainloop);
 
    /// 启动一个新进程进行调试
    /// \param[in] launch_info 进程启动信息
    /// \param[in] native_delegate 调试回调接口
    /// \return 成功则返回进程实例, 失败则返回错误信息
    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
    Launch(ProcessLaunchInfo &launch_info,
           NativeDelegate &native_delegate) override;
 
    /// 附加到一个已运行的进程
    /// \param[in] pid 目标进程ID
    /// \param[in] native_delegate 调试回调接口
    /// \return 成功则返回进程实例, 失败则返回错误信息
    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
    Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override;
 
    /// 获取支持的扩展功能
    Extension GetSupportedExtensions() const override;
 
    /// 添加进程到管理列表
    void AddProcess(NativeProcessLinux &process) {
      m_processes.insert(&process);
    }
 
    /// 从管理列表移除进程
    void RemoveProcess(NativeProcessLinux &process) {
      m_processes.erase(&process);
    }
 
    /// 收集指定线程的事件, 必要时等待事件发生
    void CollectThread(::pid_t tid);
 
  private:
    /// 处理SIGCHLD信号的句柄
    MainLoop::SignalHandleUP m_sigchld_handle;
 
    /// 正在管理的进程集合
    llvm::SmallPtrSet<NativeProcessLinux *, 2> m_processes;
 
    /// 尚未被任何进程认领的线程(事件)
    llvm::DenseSet<::pid_t> m_unowned_threads;
 
    /// SIGCHLD信号处理函数
    void SigchldHandler();
  };
 
  // NativeProcessProtocol接口实现
 
  ~NativeProcessLinux() override { m_manager.RemoveProcess(*this); }
 
  /// 恢复进程执行, 根据resume_actions指定每个线程的恢复方式
  Status Resume(const ResumeActionList &resume_actions) override;
 
  /// 暂停进程执行
  Status Halt() override;
 
  /// 与被调试进程分离(停止调试但不终止进程)
  Status Detach() override;
 
  /// 向进程发送信号
  Status Signal(int signo) override;
 
  /// 中断进程执行
  Status Interrupt() override;
 
  /// 终止被调试进程
  Status Kill() override;
 
  /// 获取指定内存地址的内存区域信息
  Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                             MemoryRegionInfo &range_info) override;
 
  /// 从进程内存中读取数据
  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
                    size_t &bytes_read) override;
 
  /// 向进程内存中写入数据
  Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
                     size_t &bytes_written) override;
 
  /// 在被调试进程中分配内存
  llvm::Expected<lldb::addr_t> AllocateMemory(size_t size,
                                              uint32_t permissions) override;
 
  /// 释放被调试进程中已分配的内存
  llvm::Error DeallocateMemory(lldb::addr_t addr) override;
 
  /// 读取内存标签
  Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
                        std::vector<uint8_t> &tags) override;
 
  /// 写入内存标签
  Status WriteMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
                         const std::vector<uint8_t> &tags) override;
 
  /// 更新线程列表, 返回更新后的线程数量
  size_t UpdateThreads() override;
 
  /// 获取进程的架构信息
  const ArchSpec &GetArchitecture() const override { return m_arch; }
 
  /// 设置断点
  /// \param[in] addr 断点地址
  /// \param[in] size 断点大小
  /// \param[in] hardware 是否使用硬件断点
  Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
                       bool hardware) override;
 
  /// 移除断点
  Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override;
 
  /// 处理停止ID更新
  void DoStopIDBumped(uint32_t newBumpId) override;
 
  /// 获取已加载模块的文件路径
  Status GetLoadedModuleFileSpec(const char *module_path,
                                 FileSpec &file_spec) override;
 
  /// 获取文件的加载地址
  Status GetFileLoadAddress(const llvm::StringRef &file_name,
                            lldb::addr_t &load_addr) override;
 
  /// 通过线程ID获取线程对象
  NativeThreadLinux *GetThreadByID(lldb::tid_t id);
  /// 获取当前线程
  NativeThreadLinux *GetCurrentThread();
 
  /// 获取auxv数据(辅助向量, 包含进程启动信息)
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
  GetAuxvData() const override {
    return getProcFile(GetID(), "auxv");
  }
 
  /// 跟踪相关方法
  /// 这些方法实现jLLDBTrace协议包
  /// \{
  /// 开始跟踪
  llvm::Error TraceStart(llvm::StringRef json_request,
                         llvm::StringRef type) override;
 
  /// 停止跟踪
  llvm::Error TraceStop(const TraceStopRequest &request) override;
 
  /// 获取跟踪状态
  llvm::Expected<llvm::json::Value>
  TraceGetState(llvm::StringRef type) override;
 
  /// 获取跟踪的二进制数据
  llvm::Expected<std::vector<uint8_t>>
  TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override;
 
  /// 获取支持的跟踪功能
  llvm::Expected<TraceSupportedResponse> TraceSupported() override;
  /// \}
 
  // 供NativeRegisterContext派生类使用的接口
  /// ptrace系统调用的封装函数
  static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
                              void *data = nullptr, size_t data_size = 0,
                              long *result = nullptr);
 
  /// 检查是否支持硬件单步执行
  bool SupportHardwareSingleStepping() const;
 
  /// 将指定线程的siginfo_t结构写入到siginfo指向的内存区域
  Status GetSignalInfo(lldb::tid_t tid, void *siginfo) const;
 
protected:
  /// 获取软件断点的陷阱指令
  llvm::Expected<llvm::ArrayRef<uint8_t>>
  GetSoftwareBreakpointTrapOpcode(size_t size_hint) override;
 
  /// 调用系统调用
  llvm::Expected<uint64_t> Syscall(llvm::ArrayRef<uint64_t> args);
 
private:
  /// 进程管理器引用
  Manager &m_manager;
  /// 架构信息
  ArchSpec m_arch;
 
  /// 内存区域支持状态(延迟计算)
  LazyBool m_supports_mem_region = eLazyBoolCalculate;
  /// 内存区域缓存
  std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
 
  /// 等待通知的线程ID
  lldb::tid_t m_pending_notification_tid = LLDB_INVALID_THREAD_ID;
 
  /// 被调试进程中分配的内存(地址到大小的映射)
  llvm::DenseMap<lldb::addr_t, lldb::addr_t> m_allocated_memory;
 
  // 私有实例方法
  /// 构造函数
  NativeProcessLinux(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
                     const ArchSpec &arch, Manager &manager,
                     llvm::ArrayRef<::pid_t> tids);
 
  /// 附加到进程, 返回已附加的线程列表
  static llvm::Expected<std::vector<::pid_t>> Attach(::pid_t pid);
 
  /// 设置默认的ptrace选项
  static Status SetDefaultPtraceOpts(const lldb::pid_t);
 
  /// 处理等待状态
  bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status);
 
  /// 监视回调函数
  void MonitorCallback(NativeThreadLinux &thread, WaitStatus status);
 
  /// 处理SIGTRAP信号
  void MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread);
 
  /// 处理跟踪事件
  void MonitorTrace(NativeThreadLinux &thread);
 
  /// 处理断点事件
  void MonitorBreakpoint(NativeThreadLinux &thread);
 
  /// 处理监视点事件
  void MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index);
 
  /// 处理信号事件
  void MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread);
 
  /// 检查是否存在指定线程(无锁版本)
  bool HasThreadNoLock(lldb::tid_t thread_id);
 
  /// 停止跟踪指定线程
  void StopTrackingThread(NativeThreadLinux &thread);
 
  /// 创建新线程
  ///
  /// 如果进程跟踪已启用且线程无法被跟踪,
  /// 则线程会以eStopReasonProcessorTrace状态停止,
  /// 并使整个进程停止
  ///
  /// \param[in] resume
  ///     如果没有跟踪错误, 若为true则创建后恢复线程执行,
  ///     若为false则使线程以SIGSTOP状态停止
  NativeThreadLinux &AddThread(lldb::tid_t thread_id, bool resume);
 
  /// 如果进程跟踪已启用, 开始跟踪新线程
  ///
  /// 跟踪机制应修改此方法以提供新线程的自动跟踪
  Status NotifyTracersOfNewThread(lldb::tid_t tid);
 
  /// 在线程销毁事件中停止跟踪
  ///
  /// 跟踪机制应修改此方法以提供销毁线程的自动跟踪停止
  Status NotifyTracersOfThreadDestroyed(lldb::tid_t tid);
 
  /// 通知跟踪器进程即将恢复执行
  void NotifyTracersProcessWillResume() override;
 
  /// 通知跟踪器进程已停止
  void NotifyTracersProcessDidStop() override;
 
  /// 将与指定线程ID对应的原始事件消息代码(对应PTRACE_GETEVENTMSG)
  /// 写入到message指向的内存
  Status GetEventMessage(lldb::tid_t tid, unsigned long *message);
 
  /// 通知线程死亡
  void NotifyThreadDeath(lldb::tid_t tid);
 
  /// 与指定线程分离
  Status Detach(lldb::tid_t tid);
 
  /// 请求所有仍在运行的线程停止.设置延迟的代理通知,
  /// 当所有线程报告停止后触发.triggering_tid将被设为当前线程(主要停止原因)
  void StopRunningThreads(lldb::tid_t triggering_tid);
 
  /// 如果所有线程都已停止, 通知代理
  void SignalIfAllThreadsStopped();
 
  /// 恢复指定线程, 可选择传递信号.恢复操作类型(继续、单步)取决于state参数
  Status ResumeThread(NativeThreadLinux &thread, lldb::StateType state,
                      int signo);
 
  /// 线程已创建的处理
  void ThreadWasCreated(NativeThreadLinux &thread);
 
  /// SIGCHLD信号处理
  void SigchldHandler();
 
  /// 填充内存区域缓存
  Status PopulateMemoryRegionCache();
 
  /// 管理Intel PT进程和线程跟踪
  IntelPTCollector m_intel_pt_collector;
 
  /// 处理类似clone()的事件
  bool MonitorClone(NativeThreadLinux &parent, lldb::pid_t child_pid,
                    int event);
};
 
} // namespace process_linux
} // namespace lldb_private
namespace lldb_private {
class Status;
class Scalar;
 
namespace process_linux {
/// \class NativeProcessLinux
/// 管理与被调试进程(inferior)的通信
///
/// 构造时, 该类准备并启动一个用于调试的被调试进程
///
/// 被调试进程状态的变化会被广播出去
class NativeProcessLinux : public NativeProcessELF,
                           private NativeProcessSoftwareSingleStep {
public:
  class Manager : public NativeProcessProtocol::Manager {
  public:
    Manager(MainLoop &mainloop);
 
    /// 启动一个新进程进行调试
    /// \param[in] launch_info 进程启动信息
    /// \param[in] native_delegate 调试回调接口
    /// \return 成功则返回进程实例, 失败则返回错误信息
    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
    Launch(ProcessLaunchInfo &launch_info,
           NativeDelegate &native_delegate) override;
 
    /// 附加到一个已运行的进程
    /// \param[in] pid 目标进程ID
    /// \param[in] native_delegate 调试回调接口
    /// \return 成功则返回进程实例, 失败则返回错误信息
    llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
    Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override;
 
    /// 获取支持的扩展功能
    Extension GetSupportedExtensions() const override;
 
    /// 添加进程到管理列表
    void AddProcess(NativeProcessLinux &process) {
      m_processes.insert(&process);
    }
 
    /// 从管理列表移除进程
    void RemoveProcess(NativeProcessLinux &process) {
      m_processes.erase(&process);
    }
 
    /// 收集指定线程的事件, 必要时等待事件发生
    void CollectThread(::pid_t tid);
 
  private:
    /// 处理SIGCHLD信号的句柄
    MainLoop::SignalHandleUP m_sigchld_handle;
 
    /// 正在管理的进程集合
    llvm::SmallPtrSet<NativeProcessLinux *, 2> m_processes;
 
    /// 尚未被任何进程认领的线程(事件)
    llvm::DenseSet<::pid_t> m_unowned_threads;
 
    /// SIGCHLD信号处理函数
    void SigchldHandler();
  };
 
  // NativeProcessProtocol接口实现
 
  ~NativeProcessLinux() override { m_manager.RemoveProcess(*this); }
 
  /// 恢复进程执行, 根据resume_actions指定每个线程的恢复方式
  Status Resume(const ResumeActionList &resume_actions) override;
 
  /// 暂停进程执行
  Status Halt() override;
 
  /// 与被调试进程分离(停止调试但不终止进程)
  Status Detach() override;
 
  /// 向进程发送信号
  Status Signal(int signo) override;
 
  /// 中断进程执行
  Status Interrupt() override;
 
  /// 终止被调试进程
  Status Kill() override;
 
  /// 获取指定内存地址的内存区域信息
  Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                             MemoryRegionInfo &range_info) override;
 
  /// 从进程内存中读取数据
  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
                    size_t &bytes_read) override;
 
  /// 向进程内存中写入数据
  Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
                     size_t &bytes_written) override;
 
  /// 在被调试进程中分配内存
  llvm::Expected<lldb::addr_t> AllocateMemory(size_t size,
                                              uint32_t permissions) override;
 
  /// 释放被调试进程中已分配的内存
  llvm::Error DeallocateMemory(lldb::addr_t addr) override;
 
  /// 读取内存标签
  Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
                        std::vector<uint8_t> &tags) override;
 
  /// 写入内存标签
  Status WriteMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
                         const std::vector<uint8_t> &tags) override;
 
  /// 更新线程列表, 返回更新后的线程数量
  size_t UpdateThreads() override;
 
  /// 获取进程的架构信息
  const ArchSpec &GetArchitecture() const override { return m_arch; }
 
  /// 设置断点
  /// \param[in] addr 断点地址
  /// \param[in] size 断点大小
  /// \param[in] hardware 是否使用硬件断点
  Status SetBreakpoint(lldb::addr_t addr, uint32_t size,
                       bool hardware) override;
 
  /// 移除断点
  Status RemoveBreakpoint(lldb::addr_t addr, bool hardware = false) override;
 
  /// 处理停止ID更新
  void DoStopIDBumped(uint32_t newBumpId) override;
 
  /// 获取已加载模块的文件路径
  Status GetLoadedModuleFileSpec(const char *module_path,
                                 FileSpec &file_spec) override;
 
  /// 获取文件的加载地址
  Status GetFileLoadAddress(const llvm::StringRef &file_name,
                            lldb::addr_t &load_addr) override;
 
  /// 通过线程ID获取线程对象
  NativeThreadLinux *GetThreadByID(lldb::tid_t id);
  /// 获取当前线程
  NativeThreadLinux *GetCurrentThread();
 
  /// 获取auxv数据(辅助向量, 包含进程启动信息)
  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>>
  GetAuxvData() const override {
    return getProcFile(GetID(), "auxv");
  }
 
  /// 跟踪相关方法
  /// 这些方法实现jLLDBTrace协议包
  /// \{
  /// 开始跟踪
  llvm::Error TraceStart(llvm::StringRef json_request,
                         llvm::StringRef type) override;
 
  /// 停止跟踪
  llvm::Error TraceStop(const TraceStopRequest &request) override;
 
  /// 获取跟踪状态
  llvm::Expected<llvm::json::Value>
  TraceGetState(llvm::StringRef type) override;
 
  /// 获取跟踪的二进制数据
  llvm::Expected<std::vector<uint8_t>>
  TraceGetBinaryData(const TraceGetBinaryDataRequest &request) override;
 
  /// 获取支持的跟踪功能
  llvm::Expected<TraceSupportedResponse> TraceSupported() override;
  /// \}
 
  // 供NativeRegisterContext派生类使用的接口
  /// ptrace系统调用的封装函数
  static Status PtraceWrapper(int req, lldb::pid_t pid, void *addr = nullptr,
                              void *data = nullptr, size_t data_size = 0,
                              long *result = nullptr);
 
  /// 检查是否支持硬件单步执行
  bool SupportHardwareSingleStepping() const;
 
  /// 将指定线程的siginfo_t结构写入到siginfo指向的内存区域
  Status GetSignalInfo(lldb::tid_t tid, void *siginfo) const;
 
protected:
  /// 获取软件断点的陷阱指令
  llvm::Expected<llvm::ArrayRef<uint8_t>>
  GetSoftwareBreakpointTrapOpcode(size_t size_hint) override;
 
  /// 调用系统调用
  llvm::Expected<uint64_t> Syscall(llvm::ArrayRef<uint64_t> args);
 
private:
  /// 进程管理器引用
  Manager &m_manager;
  /// 架构信息
  ArchSpec m_arch;
 
  /// 内存区域支持状态(延迟计算)
  LazyBool m_supports_mem_region = eLazyBoolCalculate;
  /// 内存区域缓存
  std::vector<std::pair<MemoryRegionInfo, FileSpec>> m_mem_region_cache;
 
  /// 等待通知的线程ID
  lldb::tid_t m_pending_notification_tid = LLDB_INVALID_THREAD_ID;
 
  /// 被调试进程中分配的内存(地址到大小的映射)
  llvm::DenseMap<lldb::addr_t, lldb::addr_t> m_allocated_memory;
 
  // 私有实例方法
  /// 构造函数
  NativeProcessLinux(::pid_t pid, int terminal_fd, NativeDelegate &delegate,
                     const ArchSpec &arch, Manager &manager,
                     llvm::ArrayRef<::pid_t> tids);
 
  /// 附加到进程, 返回已附加的线程列表
  static llvm::Expected<std::vector<::pid_t>> Attach(::pid_t pid);
 
  /// 设置默认的ptrace选项
  static Status SetDefaultPtraceOpts(const lldb::pid_t);
 
  /// 处理等待状态
  bool TryHandleWaitStatus(lldb::pid_t pid, WaitStatus status);
 
  /// 监视回调函数
  void MonitorCallback(NativeThreadLinux &thread, WaitStatus status);
 
  /// 处理SIGTRAP信号
  void MonitorSIGTRAP(const siginfo_t &info, NativeThreadLinux &thread);
 
  /// 处理跟踪事件
  void MonitorTrace(NativeThreadLinux &thread);
 
  /// 处理断点事件
  void MonitorBreakpoint(NativeThreadLinux &thread);
 
  /// 处理监视点事件
  void MonitorWatchpoint(NativeThreadLinux &thread, uint32_t wp_index);
 
  /// 处理信号事件
  void MonitorSignal(const siginfo_t &info, NativeThreadLinux &thread);
 
  /// 检查是否存在指定线程(无锁版本)
  bool HasThreadNoLock(lldb::tid_t thread_id);
 
  /// 停止跟踪指定线程
  void StopTrackingThread(NativeThreadLinux &thread);
 
  /// 创建新线程
  ///
  /// 如果进程跟踪已启用且线程无法被跟踪,
  /// 则线程会以eStopReasonProcessorTrace状态停止,
  /// 并使整个进程停止
  ///
  /// \param[in] resume
  ///     如果没有跟踪错误, 若为true则创建后恢复线程执行,
  ///     若为false则使线程以SIGSTOP状态停止
  NativeThreadLinux &AddThread(lldb::tid_t thread_id, bool resume);
 
  /// 如果进程跟踪已启用, 开始跟踪新线程
  ///
  /// 跟踪机制应修改此方法以提供新线程的自动跟踪
  Status NotifyTracersOfNewThread(lldb::tid_t tid);
 
  /// 在线程销毁事件中停止跟踪
  ///
  /// 跟踪机制应修改此方法以提供销毁线程的自动跟踪停止
  Status NotifyTracersOfThreadDestroyed(lldb::tid_t tid);
 
  /// 通知跟踪器进程即将恢复执行
  void NotifyTracersProcessWillResume() override;
 
  /// 通知跟踪器进程已停止
  void NotifyTracersProcessDidStop() override;
 
  /// 将与指定线程ID对应的原始事件消息代码(对应PTRACE_GETEVENTMSG)
  /// 写入到message指向的内存
  Status GetEventMessage(lldb::tid_t tid, unsigned long *message);
 
  /// 通知线程死亡
  void NotifyThreadDeath(lldb::tid_t tid);
 
  /// 与指定线程分离
  Status Detach(lldb::tid_t tid);
 
  /// 请求所有仍在运行的线程停止.设置延迟的代理通知,
  /// 当所有线程报告停止后触发.triggering_tid将被设为当前线程(主要停止原因)
  void StopRunningThreads(lldb::tid_t triggering_tid);
 
  /// 如果所有线程都已停止, 通知代理
  void SignalIfAllThreadsStopped();
 
  /// 恢复指定线程, 可选择传递信号.恢复操作类型(继续、单步)取决于state参数
  Status ResumeThread(NativeThreadLinux &thread, lldb::StateType state,
                      int signo);
 
  /// 线程已创建的处理
  void ThreadWasCreated(NativeThreadLinux &thread);
 
  /// SIGCHLD信号处理
  void SigchldHandler();
 
  /// 填充内存区域缓存
  Status PopulateMemoryRegionCache();
 
  /// 管理Intel PT进程和线程跟踪
  IntelPTCollector m_intel_pt_collector;
 
  /// 处理类似clone()的事件
  bool MonitorClone(NativeThreadLinux &parent, lldb::pid_t child_pid,
                    int event);
};
 
} // namespace process_linux
} // namespace lldb_private
/// 启动新的被调试进程(inferior)并初始化调试环境
/// \param[in] launch_info 进程启动参数(包含可执行路径、命令行参数、环境变量等)
/// \param[in] native_delegate 调试事件回调接口(用于通知进程状态变化、线程创建等)
/// \return 成功则返回封装了 NativeProcessLinux 实例的智能指针, 失败则返回错误信息
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
NativeProcessLinux::Manager::Launch(ProcessLaunchInfo &launch_info,
                                    NativeDelegate &native_delegate) {
  // 获取 POSIX 进程相关的日志对象(用于调试日志输出)
  Log *log = GetLog(POSIXLog::Process);
 
  // 记录进程启动参数到日志(如可执行路径、命令行参数等, 便于调试排查)
  MaybeLogLaunchInfo(launch_info);
 
  Status status; // 用于存储操作状态(成功/失败及错误信息)
  // 1. 通过 POSIX fork 机制启动新进程
  // ProcessLauncherPosixFork 是 POSIX 平台的进程启动器, 内部通过 fork/exec 流程创建新进程
  // LaunchProcess 会根据 launch_info 配置新进程, 并返回进程 ID(pid)
  ::pid_t pid = ProcessLauncherPosixFork()
                    .LaunchProcess(launch_info, status)
                    .GetProcessId();
   
  // 记录新进程的 PID 到日志
  LLDB_LOG(log, "pid = {0:x}", pid);
   
  // 检查进程启动是否失败(如可执行文件不存在、权限不足等)
  if (status.Fail()) {
    LLDB_LOG(log, "failed to launch process: {0}", status);
    // 将 Status 错误转换为 llvm::Error 返回(符合方法返回类型要求)
    return status.ToError();
  }
 
  // 2. 等待子进程(被调试进程)在 execve 调用时触发停止
  // 新进程启动后会执行 execve 加载目标程序, 而由于 ptrace(PTRACE_TRACEME) 配置,
  // execve 会触发 SIGTRAP 使进程停止, 此时父进程(LLDB)需要等待该停止事件
  int wstatus = 0; // 存储 waitpid 的返回状态(用于判断进程状态)
  // 调用 waitpid 等待指定 PID 的进程状态变化, RetryAfterSignal 处理被信号中断的情况
  ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0);
   
  // 断言:确保等待到的是目标进程(防止出现逻辑错误导致等待到其他进程)
  assert(wpid == pid);
  (void)wpid; // 消除未使用变量的编译警告(release 模式下 assert 可能被禁用)
 
  // 检查进程是否因停止事件(如 SIGTRAP)而暂停, 若不是则调试同步失败
  if (!WIFSTOPPED(wstatus)) {
    // 解码 wstatus 得到具体状态(如退出、信号终止等)并记录日志
    LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}",
             WaitStatus::Decode(wstatus));
    // 返回同步失败的错误信息
    return llvm::make_error<StringError>("Could not sync with inferior process",
                                         llvm::inconvertibleErrorCode());
  }
  LLDB_LOG(log, "inferior started, now in stopped state"); // 日志记录:被调试进程已启动并停止
 
  // 3. 为被调试进程设置默认的 ptrace 选项
  // 例如设置 PTRACE_O_TRACECLONE、PTRACE_O_TRACEEXEC 等, 确保能跟踪线程创建、exec 等事件
  status = SetDefaultPtraceOpts(pid);
  if (status.Fail()) {
    LLDB_LOG(log, "failed to set default ptrace options: {0}", status);
    return status.ToError();
  }
 
  // 4. 确定被调试进程的架构信息(如 x86_64、ARM64、RISCV64 等)
  llvm::Expected<ArchSpec> arch_or =
      NativeRegisterContextLinux::DetermineArchitecture(pid);
  // 若架构判断失败返回错误
  if (!arch_or)
    return arch_or.takeError();
 
  // 5. 创建并返回 NativeProcessLinux 实例
  // 释放 PTY 主文件描述符(用于被调试进程的标准输入输出交互), 传递给构造函数
  // 初始线程列表仅包含主线程(PID 与 TID 相同, Linux 中主线程 TID 等于进程 PID)
  return std::unique_ptr<NativeProcessLinux>(new NativeProcessLinux(
      pid,                                  // 被调试进程 PID
      launch_info.GetPTY().ReleasePrimaryFileDescriptor(), // PTY 主文件描述符
      native_delegate,                      // 调试事件回调接口
      *arch_or,                             // 进程架构信息
      *this,                                // 当前 Manager 实例引用(用于进程管理)
      {pid}                                 // 初始线程列表(主线程 TID)
  ));
}
/// 启动新的被调试进程(inferior)并初始化调试环境
/// \param[in] launch_info 进程启动参数(包含可执行路径、命令行参数、环境变量等)
/// \param[in] native_delegate 调试事件回调接口(用于通知进程状态变化、线程创建等)
/// \return 成功则返回封装了 NativeProcessLinux 实例的智能指针, 失败则返回错误信息
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
NativeProcessLinux::Manager::Launch(ProcessLaunchInfo &launch_info,
                                    NativeDelegate &native_delegate) {
  // 获取 POSIX 进程相关的日志对象(用于调试日志输出)
  Log *log = GetLog(POSIXLog::Process);
 
  // 记录进程启动参数到日志(如可执行路径、命令行参数等, 便于调试排查)
  MaybeLogLaunchInfo(launch_info);
 
  Status status; // 用于存储操作状态(成功/失败及错误信息)
  // 1. 通过 POSIX fork 机制启动新进程
  // ProcessLauncherPosixFork 是 POSIX 平台的进程启动器, 内部通过 fork/exec 流程创建新进程
  // LaunchProcess 会根据 launch_info 配置新进程, 并返回进程 ID(pid)
  ::pid_t pid = ProcessLauncherPosixFork()
                    .LaunchProcess(launch_info, status)
                    .GetProcessId();
   
  // 记录新进程的 PID 到日志
  LLDB_LOG(log, "pid = {0:x}", pid);
   
  // 检查进程启动是否失败(如可执行文件不存在、权限不足等)
  if (status.Fail()) {
    LLDB_LOG(log, "failed to launch process: {0}", status);
    // 将 Status 错误转换为 llvm::Error 返回(符合方法返回类型要求)
    return status.ToError();
  }
 
  // 2. 等待子进程(被调试进程)在 execve 调用时触发停止
  // 新进程启动后会执行 execve 加载目标程序, 而由于 ptrace(PTRACE_TRACEME) 配置,
  // execve 会触发 SIGTRAP 使进程停止, 此时父进程(LLDB)需要等待该停止事件
  int wstatus = 0; // 存储 waitpid 的返回状态(用于判断进程状态)
  // 调用 waitpid 等待指定 PID 的进程状态变化, RetryAfterSignal 处理被信号中断的情况
  ::pid_t wpid = llvm::sys::RetryAfterSignal(-1, ::waitpid, pid, &wstatus, 0);
   
  // 断言:确保等待到的是目标进程(防止出现逻辑错误导致等待到其他进程)
  assert(wpid == pid);
  (void)wpid; // 消除未使用变量的编译警告(release 模式下 assert 可能被禁用)
 
  // 检查进程是否因停止事件(如 SIGTRAP)而暂停, 若不是则调试同步失败
  if (!WIFSTOPPED(wstatus)) {
    // 解码 wstatus 得到具体状态(如退出、信号终止等)并记录日志
    LLDB_LOG(log, "Could not sync with inferior process: wstatus={1}",
             WaitStatus::Decode(wstatus));
    // 返回同步失败的错误信息
    return llvm::make_error<StringError>("Could not sync with inferior process",
                                         llvm::inconvertibleErrorCode());
  }
  LLDB_LOG(log, "inferior started, now in stopped state"); // 日志记录:被调试进程已启动并停止
 
  // 3. 为被调试进程设置默认的 ptrace 选项
  // 例如设置 PTRACE_O_TRACECLONE、PTRACE_O_TRACEEXEC 等, 确保能跟踪线程创建、exec 等事件
  status = SetDefaultPtraceOpts(pid);
  if (status.Fail()) {
    LLDB_LOG(log, "failed to set default ptrace options: {0}", status);
    return status.ToError();
  }
 
  // 4. 确定被调试进程的架构信息(如 x86_64、ARM64、RISCV64 等)
  llvm::Expected<ArchSpec> arch_or =
      NativeRegisterContextLinux::DetermineArchitecture(pid);
  // 若架构判断失败返回错误
  if (!arch_or)
    return arch_or.takeError();
 
  // 5. 创建并返回 NativeProcessLinux 实例
  // 释放 PTY 主文件描述符(用于被调试进程的标准输入输出交互), 传递给构造函数
  // 初始线程列表仅包含主线程(PID 与 TID 相同, Linux 中主线程 TID 等于进程 PID)
  return std::unique_ptr<NativeProcessLinux>(new NativeProcessLinux(
      pid,                                  // 被调试进程 PID
      launch_info.GetPTY().ReleasePrimaryFileDescriptor(), // PTY 主文件描述符
      native_delegate,                      // 调试事件回调接口
      *arch_or,                             // 进程架构信息
      *this,                                // 当前 Manager 实例引用(用于进程管理)
      {pid}                                 // 初始线程列表(主线程 TID)
  ));
}
/// 使用 POSIX 的 fork 机制启动新进程, 并返回进程句柄
/// \param[in] launch_info 进程启动参数(包含可执行路径、命令行参数、环境变量等)
/// \param[out] error 输出参数, 用于返回启动过程中的错误信息
/// \return 成功则返回新进程的 HostProcess 对象, 失败则返回空对象
HostProcess
ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
                                        Status &error) {
  // 创建一个管道(pipe), 用于子进程向父进程报告启动过程中的错误
  PipePosix pipe;
  // 子进程是否继承管道文件描述符:这里设为 false, 避免子进程意外操作管道
  const bool child_processes_inherit = false;
  // 初始化管道, 若失败则返回空进程
  error = pipe.CreateNew(child_processes_inherit);
  if (error.Fail())
    return HostProcess();
 
  // 将通用启动参数转换为 fork 专用的启动信息(包含环境变量、工作目录等细节)
  const ForkLaunchInfo fork_launch_info(launch_info);
 
  // 调用 POSIX 的 fork() 创建子进程
  ::pid_t pid = ::fork();
  if (pid == -1) {
    // fork 失败:记录错误信息(如权限不足、系统资源耗尽等)
    error.SetErrorStringWithFormatv("Fork failed with error message: {0}",
                                    llvm::sys::StrError());
    return HostProcess(LLDB_INVALID_PROCESS_ID);
  }
 
  if (pid == 0) {
    // 子进程执行分支
    // 关闭子进程中管道的读端(子进程只需要写端来报告错误)
    pipe.CloseReadFileDescriptor();
    // 调用子进程处理函数:执行程序加载(execve)并处理可能的错误
    // 传入管道的写端文件描述符, 用于出错时向父进程写错误信息
    ChildFunc(pipe.ReleaseWriteFileDescriptor(), fork_launch_info);
  }
 
  // 父进程执行分支
 
  // 关闭父进程中管道的写端(父进程只需要读端来接收子进程的错误信息)
  pipe.CloseWriteFileDescriptor();
 
  // 读取子进程通过管道发送的错误信息(如果有的话)
  llvm::SmallString<0> buf; // 动态缓冲区, 用于存储错误信息
  size_t pos = 0;           // 当前已读取的字节数
  ssize_t r = 0;            // 单次 read 调用的返回值
  do {
    pos += r;
    // 扩展缓冲区大小(每次增加 100 字节, 避免频繁扩容)
    buf.resize_for_overwrite(pos + 100);
    // 读取管道内容:RetryAfterSignal 处理被信号中断的情况
    r = llvm::sys::RetryAfterSignal(-1, read, pipe.GetReadFileDescriptor(),
                                    buf.begin() + pos, buf.size() - pos);
  } while (r > 0); // 持续读取直到子进程关闭写端(r=0)或出错(r=-1)
  assert(r != -1); // 断言读取过程无错误(若失败则触发调试断点)
 
  // 调整缓冲区大小为实际读取的字节数
  buf.resize(pos);
 
  // 如果缓冲区为空, 说明子进程启动成功(未发送错误信息)
  if (buf.empty())
    return HostProcess(pid); // 返回包含子进程 PID 的 HostProcess 对象
 
  // 若缓冲区非空, 说明子进程启动失败:将错误信息存入 error
  error.SetErrorString(buf);
 
  // 等待子进程退出(避免僵尸进程):忽略退出状态(已通过管道获取错误信息)
  llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
 
  // 返回空进程对象, 表示启动失败
  return HostProcess();
}
/// 使用 POSIX 的 fork 机制启动新进程, 并返回进程句柄
/// \param[in] launch_info 进程启动参数(包含可执行路径、命令行参数、环境变量等)
/// \param[out] error 输出参数, 用于返回启动过程中的错误信息
/// \return 成功则返回新进程的 HostProcess 对象, 失败则返回空对象
HostProcess
ProcessLauncherPosixFork::LaunchProcess(const ProcessLaunchInfo &launch_info,
                                        Status &error) {
  // 创建一个管道(pipe), 用于子进程向父进程报告启动过程中的错误
  PipePosix pipe;
  // 子进程是否继承管道文件描述符:这里设为 false, 避免子进程意外操作管道
  const bool child_processes_inherit = false;
  // 初始化管道, 若失败则返回空进程
  error = pipe.CreateNew(child_processes_inherit);
  if (error.Fail())
    return HostProcess();
 
  // 将通用启动参数转换为 fork 专用的启动信息(包含环境变量、工作目录等细节)
  const ForkLaunchInfo fork_launch_info(launch_info);
 
  // 调用 POSIX 的 fork() 创建子进程
  ::pid_t pid = ::fork();
  if (pid == -1) {
    // fork 失败:记录错误信息(如权限不足、系统资源耗尽等)
    error.SetErrorStringWithFormatv("Fork failed with error message: {0}",
                                    llvm::sys::StrError());
    return HostProcess(LLDB_INVALID_PROCESS_ID);
  }
 
  if (pid == 0) {
    // 子进程执行分支
    // 关闭子进程中管道的读端(子进程只需要写端来报告错误)
    pipe.CloseReadFileDescriptor();
    // 调用子进程处理函数:执行程序加载(execve)并处理可能的错误
    // 传入管道的写端文件描述符, 用于出错时向父进程写错误信息
    ChildFunc(pipe.ReleaseWriteFileDescriptor(), fork_launch_info);
  }
 
  // 父进程执行分支
 
  // 关闭父进程中管道的写端(父进程只需要读端来接收子进程的错误信息)
  pipe.CloseWriteFileDescriptor();
 
  // 读取子进程通过管道发送的错误信息(如果有的话)
  llvm::SmallString<0> buf; // 动态缓冲区, 用于存储错误信息
  size_t pos = 0;           // 当前已读取的字节数
  ssize_t r = 0;            // 单次 read 调用的返回值
  do {
    pos += r;
    // 扩展缓冲区大小(每次增加 100 字节, 避免频繁扩容)
    buf.resize_for_overwrite(pos + 100);
    // 读取管道内容:RetryAfterSignal 处理被信号中断的情况
    r = llvm::sys::RetryAfterSignal(-1, read, pipe.GetReadFileDescriptor(),
                                    buf.begin() + pos, buf.size() - pos);
  } while (r > 0); // 持续读取直到子进程关闭写端(r=0)或出错(r=-1)
  assert(r != -1); // 断言读取过程无错误(若失败则触发调试断点)
 
  // 调整缓冲区大小为实际读取的字节数
  buf.resize(pos);
 
  // 如果缓冲区为空, 说明子进程启动成功(未发送错误信息)
  if (buf.empty())
    return HostProcess(pid); // 返回包含子进程 PID 的 HostProcess 对象
 
  // 若缓冲区非空, 说明子进程启动失败:将错误信息存入 error
  error.SetErrorString(buf);
 
  // 等待子进程退出(避免僵尸进程):忽略退出状态(已通过管道获取错误信息)
  llvm::sys::RetryAfterSignal(-1, waitpid, pid, nullptr, 0);
 
  // 返回空进程对象, 表示启动失败
  return HostProcess();
}
/// 子进程专属执行函数, 完成环境配置后加载目标程序, 执行失败则通过管道返回错误
/// [[noreturn]] 标记:函数不会返回(要么成功执行 execve 替换进程, 要么调用 ExitWithError 退出)
/// \param[in] error_fd 用于向父进程传递错误信息的管道写端文件描述符
/// \param[in] info 子进程启动配置信息(包含文件操作、工作目录、调试开关等)
[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {
  // 1. 配置进程组(若需要独立进程组)
  // 独立进程组用于避免子进程受父进程终端信号(如 Ctrl+C)影响, 常见于调试场景
  if (info.separate_process_group) {
    // setpgid(0, 0):创建新进程组, 子进程成为组长(参数 0 表示当前进程 PID)
    if (setpgid(0, 0) != 0)
      ExitWithError(error_fd, "setpgid"); // 执行失败, 向父进程报告错误后退出
  }
 
  // 2. 执行预定义的文件操作(如关闭、复制、打开文件描述符)
  // 这些操作来自 ProcessLaunchInfo, 用于配置子进程的标准输入/输出、日志文件等
  for (const ForkFileAction &action : info.actions) {
    switch (action.action) {
    case FileAction::eFileActionClose:
      // 关闭指定文件描述符(如父进程继承的无关文件句柄)
      if (close(action.fd) != 0)
        ExitWithError(error_fd, "close");
      break;
    case FileAction::eFileActionDuplicate:
      // 复制文件描述符(如将标准输出重定向到日志文件:dup2(log_fd, STDOUT_FILENO))
      if (dup2(action.fd, action.arg) == -1)
        ExitWithError(error_fd, "dup2");
      break;
    case FileAction::eFileActionOpen:
      // 打开指定路径的文件, 并将文件描述符设置为 action.fd(如打开配置文件)
      DupDescriptor(error_fd, action.path.c_str(), action.fd, action.arg);
      break;
    case FileAction::eFileActionNone:
      // 无操作, 跳过
      break;
    }
  }
 
  // 3. 切换子进程的工作目录(若配置了工作目录)
  if (!info.wd.empty() && 0 != ::chdir(info.wd.c_str()))
    ExitWithError(error_fd, "chdir"); // 切换失败(如目录不存在), 报告错误后退出
 
  // 4. 禁用地址空间随机化(ASLR)(若配置了禁用)
  // 禁用 ASLR 可让程序每次启动时内存地址固定, 便于调试(如断点地址不变)
  if (info.disable_aslr)
    DisableASLR(error_fd); // 执行禁用逻辑, 失败则报告错误
 
  // 5. 清空信号掩码, 避免父进程的信号屏蔽影响子进程
  // 父进程可能设置了信号屏蔽(如忽略某些信号), 子进程需重置为默认状态
  sigset_t set;
  if (sigemptyset(&set) != 0 || // 初始化空信号集(不屏蔽任何信号)
      pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) // 应用信号集
    ExitWithError(error_fd, "pthread_sigmask");
 
  // 6. 调试模式专属配置(若启用调试)
  if (info.debug) {
    // 6.1 放弃 setgid 权限(安全措施)
    // 若父进程有 setgid 权限(允许切换组ID), 子进程调试时主动放弃, 避免权限滥用
    if (setgid(getgid()) != 0)
      ExitWithError(error_fd, "setgid");
 
    // 6.2 关闭无关文件描述符(避免句柄泄漏)
    // 仅调试场景执行:因为调试时子进程无需继承父进程的非标准文件句柄(如日志、网络连接)
    // 注:该逻辑非信号安全, 但调试场景下子进程启动时无多线程, 可安全执行
    const llvm::StringRef proc_fd_path = "/proc/self/fd"; // 进程自身打开的文件描述符目录
    std::error_code ec;
    bool result;
    // 检查 /proc/self/fd 是否存在(Linux 系统支持, 用于遍历所有打开的 FD)
    ec = llvm::sys::fs::is_directory(proc_fd_path, result);
    if (result) {
      std::vector<int> files_to_close; // 存储需要关闭的 FD 列表
      // 遍历 /proc/self/fd 目录下的所有 FD(每个目录项对应一个打开的文件描述符)
      for (llvm::sys::fs::directory_iterator iter(proc_fd_path, ec), file_end;
           iter != file_end && !ec; iter.increment(ec)) {
        // 从路径中提取 FD 数值(如 "/proc/self/fd/5" 提取为 5)
        int fd = std::stoi(iter->path().substr(proc_fd_path.size() + 1));
 
        // 不关闭的 FD 规则:
        // 1. 前 3 个 FD(0=stdin、1=stdout、2=stderr);
        // 2. 有预定义文件操作的 FD(如调试用的 PTY 句柄);
        // 3. 错误传递管道的写端(error_fd, 用于报告错误)
        if (fd > 2 && !info.has_action(fd) && fd != error_fd)
          files_to_close.push_back(fd);
      }
      // 关闭所有标记的无关 FD
      for (int file_to_close : files_to_close)
        close(file_to_close);
    } else {
      // 若 /proc/self/fd 不可用(如非 Linux 系统), 采用备选方案:遍历可能的 FD 范围
      int max_fd = sysconf(_SC_OPEN_MAX); // 获取系统支持的最大 FD 数量
      for (int fd = 3; fd < max_fd; ++fd) // 从 FD=3 开始检查(跳过 stdin/stdout/stderr)
        if (!info.has_action(fd) && fd != error_fd)
          close(fd);
    }
 
    // 6.3 启用调试跟踪:让父进程(LLDB)成为当前子进程的调试器
    // PT_TRACE_ME:子进程声明“愿意被父进程调试”, 后续执行 execve 时会触发 SIGTRAP 信号
    if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
      ExitWithError(error_fd, "ptrace");
  }
 
  // 7. 执行目标程序:替换当前子进程的代码段、数据段(核心步骤)
  // execve(path, argv, envp):加载 path 指向的可执行文件, 使用 argv 命令行参数和 envp 环境变量
  // 若执行成功, 当前进程会被完全替换, 后续代码不会执行;若失败则进入下方错误处理
  execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);
 
  // 8. Linux 平台专属:处理 ETXTBSY 错误(可执行文件被占用)
#if defined(__linux__)
  if (errno == ETXTBSY) {
    // 错误原因:可执行文件被其他进程占用(如 Android 中 adb 守护进程未释放文件句柄)
    // 解决方案:等待 50ms(0.05 秒)后重试一次, 避免短暂占用导致的启动失败
    usleep(50000); // 微秒级睡眠(50000us = 50ms)
    execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp); // 再次尝试执行
  }
#endif
 
  // 9. execve 执行失败(到这里说明两次尝试都失败):报告错误后退出子进程
  // 常见失败原因:可执行文件不存在、权限不足、文件格式错误等
  ExitWithError(error_fd, "execve");
}
/// 子进程专属执行函数, 完成环境配置后加载目标程序, 执行失败则通过管道返回错误
/// [[noreturn]] 标记:函数不会返回(要么成功执行 execve 替换进程, 要么调用 ExitWithError 退出)
/// \param[in] error_fd 用于向父进程传递错误信息的管道写端文件描述符
/// \param[in] info 子进程启动配置信息(包含文件操作、工作目录、调试开关等)
[[noreturn]] static void ChildFunc(int error_fd, const ForkLaunchInfo &info) {
  // 1. 配置进程组(若需要独立进程组)
  // 独立进程组用于避免子进程受父进程终端信号(如 Ctrl+C)影响, 常见于调试场景
  if (info.separate_process_group) {
    // setpgid(0, 0):创建新进程组, 子进程成为组长(参数 0 表示当前进程 PID)
    if (setpgid(0, 0) != 0)
      ExitWithError(error_fd, "setpgid"); // 执行失败, 向父进程报告错误后退出
  }
 
  // 2. 执行预定义的文件操作(如关闭、复制、打开文件描述符)
  // 这些操作来自 ProcessLaunchInfo, 用于配置子进程的标准输入/输出、日志文件等
  for (const ForkFileAction &action : info.actions) {
    switch (action.action) {
    case FileAction::eFileActionClose:
      // 关闭指定文件描述符(如父进程继承的无关文件句柄)
      if (close(action.fd) != 0)
        ExitWithError(error_fd, "close");
      break;
    case FileAction::eFileActionDuplicate:
      // 复制文件描述符(如将标准输出重定向到日志文件:dup2(log_fd, STDOUT_FILENO))
      if (dup2(action.fd, action.arg) == -1)
        ExitWithError(error_fd, "dup2");
      break;
    case FileAction::eFileActionOpen:
      // 打开指定路径的文件, 并将文件描述符设置为 action.fd(如打开配置文件)
      DupDescriptor(error_fd, action.path.c_str(), action.fd, action.arg);
      break;
    case FileAction::eFileActionNone:
      // 无操作, 跳过
      break;
    }
  }
 
  // 3. 切换子进程的工作目录(若配置了工作目录)
  if (!info.wd.empty() && 0 != ::chdir(info.wd.c_str()))
    ExitWithError(error_fd, "chdir"); // 切换失败(如目录不存在), 报告错误后退出
 
  // 4. 禁用地址空间随机化(ASLR)(若配置了禁用)
  // 禁用 ASLR 可让程序每次启动时内存地址固定, 便于调试(如断点地址不变)
  if (info.disable_aslr)
    DisableASLR(error_fd); // 执行禁用逻辑, 失败则报告错误
 
  // 5. 清空信号掩码, 避免父进程的信号屏蔽影响子进程
  // 父进程可能设置了信号屏蔽(如忽略某些信号), 子进程需重置为默认状态
  sigset_t set;
  if (sigemptyset(&set) != 0 || // 初始化空信号集(不屏蔽任何信号)
      pthread_sigmask(SIG_SETMASK, &set, nullptr) != 0) // 应用信号集
    ExitWithError(error_fd, "pthread_sigmask");
 
  // 6. 调试模式专属配置(若启用调试)
  if (info.debug) {
    // 6.1 放弃 setgid 权限(安全措施)
    // 若父进程有 setgid 权限(允许切换组ID), 子进程调试时主动放弃, 避免权限滥用
    if (setgid(getgid()) != 0)
      ExitWithError(error_fd, "setgid");
 
    // 6.2 关闭无关文件描述符(避免句柄泄漏)
    // 仅调试场景执行:因为调试时子进程无需继承父进程的非标准文件句柄(如日志、网络连接)
    // 注:该逻辑非信号安全, 但调试场景下子进程启动时无多线程, 可安全执行
    const llvm::StringRef proc_fd_path = "/proc/self/fd"; // 进程自身打开的文件描述符目录
    std::error_code ec;
    bool result;
    // 检查 /proc/self/fd 是否存在(Linux 系统支持, 用于遍历所有打开的 FD)
    ec = llvm::sys::fs::is_directory(proc_fd_path, result);
    if (result) {
      std::vector<int> files_to_close; // 存储需要关闭的 FD 列表
      // 遍历 /proc/self/fd 目录下的所有 FD(每个目录项对应一个打开的文件描述符)
      for (llvm::sys::fs::directory_iterator iter(proc_fd_path, ec), file_end;
           iter != file_end && !ec; iter.increment(ec)) {
        // 从路径中提取 FD 数值(如 "/proc/self/fd/5" 提取为 5)
        int fd = std::stoi(iter->path().substr(proc_fd_path.size() + 1));
 
        // 不关闭的 FD 规则:
        // 1. 前 3 个 FD(0=stdin、1=stdout、2=stderr);
        // 2. 有预定义文件操作的 FD(如调试用的 PTY 句柄);
        // 3. 错误传递管道的写端(error_fd, 用于报告错误)
        if (fd > 2 && !info.has_action(fd) && fd != error_fd)
          files_to_close.push_back(fd);
      }
      // 关闭所有标记的无关 FD
      for (int file_to_close : files_to_close)
        close(file_to_close);
    } else {
      // 若 /proc/self/fd 不可用(如非 Linux 系统), 采用备选方案:遍历可能的 FD 范围
      int max_fd = sysconf(_SC_OPEN_MAX); // 获取系统支持的最大 FD 数量
      for (int fd = 3; fd < max_fd; ++fd) // 从 FD=3 开始检查(跳过 stdin/stdout/stderr)
        if (!info.has_action(fd) && fd != error_fd)
          close(fd);
    }
 
    // 6.3 启用调试跟踪:让父进程(LLDB)成为当前子进程的调试器
    // PT_TRACE_ME:子进程声明“愿意被父进程调试”, 后续执行 execve 时会触发 SIGTRAP 信号
    if (ptrace(PT_TRACE_ME, 0, nullptr, 0) == -1)
      ExitWithError(error_fd, "ptrace");
  }
 
  // 7. 执行目标程序:替换当前子进程的代码段、数据段(核心步骤)
  // execve(path, argv, envp):加载 path 指向的可执行文件, 使用 argv 命令行参数和 envp 环境变量
  // 若执行成功, 当前进程会被完全替换, 后续代码不会执行;若失败则进入下方错误处理
  execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp);
 
  // 8. Linux 平台专属:处理 ETXTBSY 错误(可执行文件被占用)
#if defined(__linux__)
  if (errno == ETXTBSY) {
    // 错误原因:可执行文件被其他进程占用(如 Android 中 adb 守护进程未释放文件句柄)
    // 解决方案:等待 50ms(0.05 秒)后重试一次, 避免短暂占用导致的启动失败
    usleep(50000); // 微秒级睡眠(50000us = 50ms)
    execve(info.argv[0], const_cast<char *const *>(info.argv), info.envp); // 再次尝试执行
  }
#endif
 
  // 9. execve 执行失败(到这里说明两次尝试都失败):报告错误后退出子进程
  // 常见失败原因:可执行文件不存在、权限不足、文件格式错误等
  ExitWithError(error_fd, "execve");
}
Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) {
  long ptrace_opts = 0;  // 用于存储ptrace选项的变量, 初始化为0
 
  // 让子进程在退出时触发一个事件.这用于将子进程保持在未终结状态, 直到它被销毁
  ptrace_opts |= PTRACE_O_TRACEEXIT;
 
  // 让跟踪器跟踪在目标进程中生成的线程
  ptrace_opts |= PTRACE_O_TRACECLONE;
 
  // 让跟踪器在execve系统调用返回前通知我们(需要用于禁用传统的SIGTRAP信号生成)
  ptrace_opts |= PTRACE_O_TRACEEXEC;
 
  // 让跟踪器跟踪fork创建的子进程
  ptrace_opts |= PTRACE_O_TRACEFORK;
 
  // 让跟踪器跟踪vfork创建的子进程
  ptrace_opts |= PTRACE_O_TRACEVFORK;
 
  // 让跟踪器跟踪vfork完成事件, 以便在子进程完成内存共享后恢复断点
  ptrace_opts |= PTRACE_O_TRACEVFORKDONE;
 
  // 调用ptrace包装函数, 应用上面设置的所有选项
  return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts);
}
 
// ptrace的包装函数, 用于捕获错误并记录调用.注意ptrace在出错时会设置errno,
// 因为-1可能是一个有效的结果(例如对于PTRACE_PEEK*操作)
Status NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
                                         void *data, size_t data_size,
                                         long *result) {
  Status error;       // 用于存储操作状态的对象
  long int ret;       // 用于存储ptrace调用的返回值
 
  // 获取日志对象, 用于记录ptrace相关操作
  Log *log = GetLog(POSIXLog::Ptrace);
 
  // 显示ptrace操作相关的字节数据(用于调试日志)
  PtraceDisplayBytes(req, data, data_size);
 
  errno = 0;  // 重置errno, 以便正确捕获ptrace的错误状态
   
  // 根据不同的请求类型调用ptrace
  if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET)
    // 对于寄存器集操作, 需要特殊处理addr参数
    ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid),
                 *(unsigned int *)addr, data);
  else
    // 普通ptrace调用
    ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid),
                 addr, data);
 
  // 如果返回值为-1, 表示发生错误, 设置错误信息
  if (ret == -1)
    error.SetErrorToErrno();
 
  // 如果提供了结果指针, 将ptrace的返回值存入
  if (result)
    *result = ret;
 
  // 记录ptrace调用的详细信息到日志
  LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data,
           data_size, ret);
 
  // 再次显示操作后的字节数据(用于调试日志)
  PtraceDisplayBytes(req, data, data_size);
 
  // 如果操作失败, 记录错误信息到日志
  if (error.Fail())
    LLDB_LOG(log, "ptrace() failed: {0}", error);
 
  return error;  // 返回操作状态
}
Status NativeProcessLinux::SetDefaultPtraceOpts(lldb::pid_t pid) {
  long ptrace_opts = 0;  // 用于存储ptrace选项的变量, 初始化为0
 
  // 让子进程在退出时触发一个事件.这用于将子进程保持在未终结状态, 直到它被销毁
  ptrace_opts |= PTRACE_O_TRACEEXIT;
 
  // 让跟踪器跟踪在目标进程中生成的线程
  ptrace_opts |= PTRACE_O_TRACECLONE;
 
  // 让跟踪器在execve系统调用返回前通知我们(需要用于禁用传统的SIGTRAP信号生成)
  ptrace_opts |= PTRACE_O_TRACEEXEC;
 
  // 让跟踪器跟踪fork创建的子进程
  ptrace_opts |= PTRACE_O_TRACEFORK;
 
  // 让跟踪器跟踪vfork创建的子进程
  ptrace_opts |= PTRACE_O_TRACEVFORK;
 
  // 让跟踪器跟踪vfork完成事件, 以便在子进程完成内存共享后恢复断点
  ptrace_opts |= PTRACE_O_TRACEVFORKDONE;
 
  // 调用ptrace包装函数, 应用上面设置的所有选项
  return PtraceWrapper(PTRACE_SETOPTIONS, pid, nullptr, (void *)ptrace_opts);
}
 
// ptrace的包装函数, 用于捕获错误并记录调用.注意ptrace在出错时会设置errno,
// 因为-1可能是一个有效的结果(例如对于PTRACE_PEEK*操作)
Status NativeProcessLinux::PtraceWrapper(int req, lldb::pid_t pid, void *addr,
                                         void *data, size_t data_size,
                                         long *result) {
  Status error;       // 用于存储操作状态的对象
  long int ret;       // 用于存储ptrace调用的返回值
 
  // 获取日志对象, 用于记录ptrace相关操作
  Log *log = GetLog(POSIXLog::Ptrace);
 
  // 显示ptrace操作相关的字节数据(用于调试日志)
  PtraceDisplayBytes(req, data, data_size);
 
  errno = 0;  // 重置errno, 以便正确捕获ptrace的错误状态
   
  // 根据不同的请求类型调用ptrace
  if (req == PTRACE_GETREGSET || req == PTRACE_SETREGSET)
    // 对于寄存器集操作, 需要特殊处理addr参数
    ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid),
                 *(unsigned int *)addr, data);
  else
    // 普通ptrace调用
    ret = ptrace(static_cast<__ptrace_request>(req), static_cast<::pid_t>(pid),
                 addr, data);
 
  // 如果返回值为-1, 表示发生错误, 设置错误信息
  if (ret == -1)
    error.SetErrorToErrno();
 
  // 如果提供了结果指针, 将ptrace的返回值存入
  if (result)
    *result = ret;
 
  // 记录ptrace调用的详细信息到日志
  LLDB_LOG(log, "ptrace({0}, {1}, {2}, {3}, {4})={5:x}", req, pid, addr, data,
           data_size, ret);
 
  // 再次显示操作后的字节数据(用于调试日志)
  PtraceDisplayBytes(req, data, data_size);
 
  // 如果操作失败, 记录错误信息到日志
  if (error.Fail())
    LLDB_LOG(log, "ptrace() failed: {0}", error);
 
  return error;  // 返回操作状态
}
// 附加到一个已运行的进程, 创建并返回NativeProcessLinux实例
// 参数:
//   pid - 要附加的进程ID
//   native_delegate - 用于接收进程事件通知的代理对象
// 返回值: 包含NativeProcessProtocol唯一指针的Expected对象, 成功则为进程实例, 失败则为错误信息
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
NativeProcessLinux::Manager::Attach(
    lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) {
  Log *log = GetLog(POSIXLog::Process);  // 获取进程相关的日志对象
  LLDB_LOG(log, "pid = {0:x}", pid);     // 记录要附加的进程ID
 
  // 尝试附加到指定PID的进程
  auto tids_or = NativeProcessLinux::Attach(pid);
  if (!tids_or)                          // 如果附加失败
    return tids_or.takeError();          // 返回错误信息
  ArrayRef<::pid_t> tids = *tids_or;     // 获取成功返回的线程ID列表
 
  // 通过进程的第一个线程ID确定目标进程的架构
  llvm::Expected<ArchSpec> arch_or =
      NativeRegisterContextLinux::DetermineArchitecture(tids[0]);
  if (!arch_or)                          // 如果架构确定失败
    return arch_or.takeError();          // 返回错误信息
 
  // 创建并返回NativeProcessLinux实例, 封装了对目标进程的调试控制
  return std::unique_ptr<NativeProcessLinux>(
      new NativeProcessLinux(pid, -1, native_delegate, *arch_or, *this, tids));
}
 
// 附加到指定PID的进程, 并返回该进程中所有线程的ID列表
// 参数: pid - 要附加的进程ID
// 返回值: 包含线程ID列表的Expected对象, 成功则为线程列表, 失败则为错误信息
llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) {
  Log *log = GetLog(POSIXLog::Process);  // 获取进程相关的日志对象
 
  Status status;
  // 使用映射表跟踪需要附加和已附加的线程(键为线程ID, 值为是否已附加)
  Host::TidMap tids_to_attach;
   
  // 查找目标进程的所有线程, 填充到tids_to_attach中
  while (Host::FindProcessThreads(pid, tids_to_attach)) {
    // 遍历所有需要处理的线程
    for (Host::TidMap::iterator it = tids_to_attach.begin();
         it != tids_to_attach.end();) {
      // 处理尚未附加的线程
      if (it->second == false) {
        lldb::tid_t tid = it->first;  // 获取当前线程ID
 
        // 附加到请求的线程, 这会导致线程收到SIGSTOP信号而停止
        if ((status = PtraceWrapper(PTRACE_ATTACH, tid)).Fail()) {
          // 线程不存在(可能已退出), 从映射表中移除并继续处理其他线程
          if (status.GetError() == ESRCH) {
            it = tids_to_attach.erase(it);
            continue;
          }
          // 没有权限附加到线程, 可能是ptrace_scope设置导致
          if (status.GetError() == EPERM) {
            // 返回包含ptrace_scope相关提示的错误信息
            return AddPtraceScopeNote(status.ToError());
          }
          // 其他错误, 直接返回错误信息
          return status.ToError();
        }
 
        // 等待线程停止(使用__WALL标志确保能等待到线程)
        int wpid =
            llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL);
        // 如果waitpid失败(返回值<0)
        if (wpid < 0) {
          // 线程不存在, 从映射表中移除并继续
          if (errno == ESRCH) {
            it = tids_to_attach.erase(it);
            continue;
          }
          // 返回系统错误信息
          return llvm::errorCodeToError(
              std::error_code(errno, std::generic_category()));
        }
 
        // 为当前线程设置默认的ptrace选项
        if ((status = SetDefaultPtraceOpts(tid)).Fail())
          return status.ToError();
 
        LLDB_LOG(log, "adding tid = {0}", tid);  // 记录已附加的线程ID
        it->second = true// 标记该线程已成功附加
      }
 
      // 移动迭代器到下一个线程
      ++it;
    }
  }
 
  // 检查是否成功附加到至少一个线程
  size_t tid_count = tids_to_attach.size();
  if (tid_count == 0)
    return llvm::make_error<StringError>("No such process",
                                         llvm::inconvertibleErrorCode());
 
  // 收集所有已附加的线程ID到向量中并返回
  std::vector<::pid_t> tids;
  tids.reserve(tid_count);  // 预分配内存提升效率
  for (const auto &p : tids_to_attach)
    tids.push_back(p.first);
  return std::move(tids);
}
// 附加到一个已运行的进程, 创建并返回NativeProcessLinux实例
// 参数:
//   pid - 要附加的进程ID
//   native_delegate - 用于接收进程事件通知的代理对象
// 返回值: 包含NativeProcessProtocol唯一指针的Expected对象, 成功则为进程实例, 失败则为错误信息
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
NativeProcessLinux::Manager::Attach(
    lldb::pid_t pid, NativeProcessProtocol::NativeDelegate &native_delegate) {
  Log *log = GetLog(POSIXLog::Process);  // 获取进程相关的日志对象
  LLDB_LOG(log, "pid = {0:x}", pid);     // 记录要附加的进程ID
 
  // 尝试附加到指定PID的进程
  auto tids_or = NativeProcessLinux::Attach(pid);
  if (!tids_or)                          // 如果附加失败
    return tids_or.takeError();          // 返回错误信息
  ArrayRef<::pid_t> tids = *tids_or;     // 获取成功返回的线程ID列表
 
  // 通过进程的第一个线程ID确定目标进程的架构
  llvm::Expected<ArchSpec> arch_or =
      NativeRegisterContextLinux::DetermineArchitecture(tids[0]);
  if (!arch_or)                          // 如果架构确定失败
    return arch_or.takeError();          // 返回错误信息
 
  // 创建并返回NativeProcessLinux实例, 封装了对目标进程的调试控制
  return std::unique_ptr<NativeProcessLinux>(
      new NativeProcessLinux(pid, -1, native_delegate, *arch_or, *this, tids));
}
 
// 附加到指定PID的进程, 并返回该进程中所有线程的ID列表
// 参数: pid - 要附加的进程ID
// 返回值: 包含线程ID列表的Expected对象, 成功则为线程列表, 失败则为错误信息
llvm::Expected<std::vector<::pid_t>> NativeProcessLinux::Attach(::pid_t pid) {
  Log *log = GetLog(POSIXLog::Process);  // 获取进程相关的日志对象
 
  Status status;
  // 使用映射表跟踪需要附加和已附加的线程(键为线程ID, 值为是否已附加)
  Host::TidMap tids_to_attach;
   
  // 查找目标进程的所有线程, 填充到tids_to_attach中
  while (Host::FindProcessThreads(pid, tids_to_attach)) {
    // 遍历所有需要处理的线程
    for (Host::TidMap::iterator it = tids_to_attach.begin();
         it != tids_to_attach.end();) {
      // 处理尚未附加的线程
      if (it->second == false) {
        lldb::tid_t tid = it->first;  // 获取当前线程ID
 
        // 附加到请求的线程, 这会导致线程收到SIGSTOP信号而停止
        if ((status = PtraceWrapper(PTRACE_ATTACH, tid)).Fail()) {
          // 线程不存在(可能已退出), 从映射表中移除并继续处理其他线程
          if (status.GetError() == ESRCH) {
            it = tids_to_attach.erase(it);
            continue;
          }
          // 没有权限附加到线程, 可能是ptrace_scope设置导致
          if (status.GetError() == EPERM) {
            // 返回包含ptrace_scope相关提示的错误信息
            return AddPtraceScopeNote(status.ToError());
          }
          // 其他错误, 直接返回错误信息
          return status.ToError();
        }
 
        // 等待线程停止(使用__WALL标志确保能等待到线程)
        int wpid =
            llvm::sys::RetryAfterSignal(-1, ::waitpid, tid, nullptr, __WALL);
        // 如果waitpid失败(返回值<0)
        if (wpid < 0) {
          // 线程不存在, 从映射表中移除并继续
          if (errno == ESRCH) {
            it = tids_to_attach.erase(it);
            continue;
          }
          // 返回系统错误信息
          return llvm::errorCodeToError(
              std::error_code(errno, std::generic_category()));
        }
 
        // 为当前线程设置默认的ptrace选项
        if ((status = SetDefaultPtraceOpts(tid)).Fail())
          return status.ToError();
 
        LLDB_LOG(log, "adding tid = {0}", tid);  // 记录已附加的线程ID
        it->second = true// 标记该线程已成功附加
      }
 
      // 移动迭代器到下一个线程
      ++it;
    }
  }
 
  // 检查是否成功附加到至少一个线程
  size_t tid_count = tids_to_attach.size();
  if (tid_count == 0)
    return llvm::make_error<StringError>("No such process",
                                         llvm::inconvertibleErrorCode());
 
  // 收集所有已附加的线程ID到向量中并返回
  std::vector<::pid_t> tids;
  tids.reserve(tid_count);  // 预分配内存提升效率
  for (const auto &p : tids_to_attach)
    tids.push_back(p.first);
  return std::move(tids);
}
/// @brief 查找指定进程(PID)下的所有线程, 并更新线程ID映射表
/// @param[in] pid 目标进程的ID, 需查找该进程下的所有线程
/// @param[inout] tids_to_attach 线程ID映射表(键:线程ID, 值:是否已附加),
///                              函数会将新发现的线程ID插入表中, 标记为“未附加”(false)
/// @return 若本次查找发现了新线程(映射表有更新)则返回true, 否则返回false
bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
  // 标记映射表是否有变化(是否新增了线程)
  bool tids_changed = false;
   
  // 静态常量:/proc/ 是Linux系统中用于查看进程信息的虚拟文件系统根路径
  static const char procdir[] = "/proc/";
  // 静态常量:/task/ 是每个进程目录下存储线程信息的子目录名(每个线程对应一个子目录)
  static const char taskdir[] = "/task/";
   
  // 拼接目标进程的线程目录路径:/proc/[pid]/task/
  // 例如pid=1234时, 路径为/proc/1234/task/, 该目录下的子目录名即为线程ID(TID)
  std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
   
  // 打开目标进程的线程目录, 获取目录句柄
  DIR *dirproc = opendir(process_task_dir.c_str());
 
  // 若成功打开线程目录(说明进程存在且有权限访问)
  if (dirproc) {
    // 目录项结构体, 用于存储每次读取到的目录内容(如子目录名、文件类型)
    struct dirent *direntry = nullptr;
     
    // 循环读取目录中的每一项内容, 直到读取完毕(direntry为nullptr)
    while ((direntry = readdir(dirproc)) != nullptr) {
      // 过滤非目录项 + 非数字命名的目录:
      // 1. direntry->d_type != DT_DIR:排除文件(线程目录下只有子目录, 每个子目录对应一个线程)
      // 2. !IsDirNumeric(direntry->d_name):排除非数字命名的目录(如.和.., 表示当前/上级目录)
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
        continue;
 
      // 将线程目录名(字符串)转换为整数型线程ID(TID)
      lldb::tid_t tid = atoi(direntry->d_name);
       
      // 在映射表中查找当前TID, 判断是否已存在
      TidMap::iterator it = tids_to_attach.find(tid);
       
      // 若TID不存在于映射表中(发现新线程)
      if (it == tids_to_attach.end()) {
        // 将新线程ID插入映射表, 标记为“未附加”(false)
        tids_to_attach.insert(TidPair(tid, false));
        // 标记映射表发生变化(新增了线程)
        tids_changed = true;
      }
    }
     
    // 关闭目录句柄, 释放资源(避免内存泄漏)
    closedir(dirproc);
  }
 
  // 返回映射表是否有更新(是否发现新线程)
  return tids_changed;
}
/// @brief 查找指定进程(PID)下的所有线程, 并更新线程ID映射表
/// @param[in] pid 目标进程的ID, 需查找该进程下的所有线程
/// @param[inout] tids_to_attach 线程ID映射表(键:线程ID, 值:是否已附加),
///                              函数会将新发现的线程ID插入表中, 标记为“未附加”(false)
/// @return 若本次查找发现了新线程(映射表有更新)则返回true, 否则返回false
bool Host::FindProcessThreads(const lldb::pid_t pid, TidMap &tids_to_attach) {
  // 标记映射表是否有变化(是否新增了线程)
  bool tids_changed = false;
   
  // 静态常量:/proc/ 是Linux系统中用于查看进程信息的虚拟文件系统根路径
  static const char procdir[] = "/proc/";
  // 静态常量:/task/ 是每个进程目录下存储线程信息的子目录名(每个线程对应一个子目录)
  static const char taskdir[] = "/task/";
   
  // 拼接目标进程的线程目录路径:/proc/[pid]/task/
  // 例如pid=1234时, 路径为/proc/1234/task/, 该目录下的子目录名即为线程ID(TID)
  std::string process_task_dir = procdir + llvm::to_string(pid) + taskdir;
   
  // 打开目标进程的线程目录, 获取目录句柄
  DIR *dirproc = opendir(process_task_dir.c_str());
 
  // 若成功打开线程目录(说明进程存在且有权限访问)
  if (dirproc) {
    // 目录项结构体, 用于存储每次读取到的目录内容(如子目录名、文件类型)
    struct dirent *direntry = nullptr;
     
    // 循环读取目录中的每一项内容, 直到读取完毕(direntry为nullptr)
    while ((direntry = readdir(dirproc)) != nullptr) {
      // 过滤非目录项 + 非数字命名的目录:
      // 1. direntry->d_type != DT_DIR:排除文件(线程目录下只有子目录, 每个子目录对应一个线程)
      // 2. !IsDirNumeric(direntry->d_name):排除非数字命名的目录(如.和.., 表示当前/上级目录)
      if (direntry->d_type != DT_DIR || !IsDirNumeric(direntry->d_name))
        continue;
 
      // 将线程目录名(字符串)转换为整数型线程ID(TID)
      lldb::tid_t tid = atoi(direntry->d_name);
       
      // 在映射表中查找当前TID, 判断是否已存在
      TidMap::iterator it = tids_to_attach.find(tid);
       
      // 若TID不存在于映射表中(发现新线程)
      if (it == tids_to_attach.end()) {
        // 将新线程ID插入映射表, 标记为“未附加”(false)
        tids_to_attach.insert(TidPair(tid, false));
        // 标记映射表发生变化(新增了线程)
        tids_changed = true;
      }
    }
     
    // 关闭目录句柄, 释放资源(避免内存泄漏)
    closedir(dirproc);
  }
 
  // 返回映射表是否有更新(是否发现新线程)
  return tids_changed;
}
Status NativeProcessLinux::Halt() {
  Status error;  // 用于存储操作结果状态
 
  // 向当前进程(GetID()返回进程ID)发送SIGSTOP信号
  // SIGSTOP是一个不能被忽略或捕获的信号, 会强制进程暂停执行
  if (kill(GetID(), SIGSTOP) != 0) {
    // 若信号发送失败(返回非0), 将系统错误码(errno)转换为Status错误信息
    error.SetErrorToErrno();
  }
 
  // 返回操作结果:成功时error为空, 失败时包含具体错误
  return error;
}
Status NativeProcessLinux::Halt() {
  Status error;  // 用于存储操作结果状态
 
  // 向当前进程(GetID()返回进程ID)发送SIGSTOP信号
  // SIGSTOP是一个不能被忽略或捕获的信号, 会强制进程暂停执行
  if (kill(GetID(), SIGSTOP) != 0) {
    // 若信号发送失败(返回非0), 将系统错误码(errno)转换为Status错误信息
    error.SetErrorToErrno();
  }
 
  // 返回操作结果:成功时error为空, 失败时包含具体错误
  return error;
}
/// \param[in] resume_actions 包含每个线程的恢复动作列表(状态和信号)
/// \return 成功返回空状态, 失败返回错误信息
Status NativeProcessLinux::Resume(const ResumeActionList &resume_actions) {
  // 获取日志对象, 用于调试日志输出(POSIX 进程相关日志)
  Log *log = GetLog(POSIXLog::Process);
  // 记录当前进程 ID 到日志
  LLDB_LOG(log, "pid {0}", GetID());
 
  // 通知跟踪器(如 Intel PT)进程即将恢复执行
  NotifyTracersProcessWillResume();
 
  // 检查当前平台是否支持硬件单步执行, 不支持则使用软件单步
  bool software_single_step = !SupportHardwareSingleStepping();
 
  // 如果需要使用软件单步执行
  if (software_single_step) {
    // 遍历所有线程, 为需要单步的线程设置软件单步环境
    for (const auto &thread : m_threads) {
      assert(thread && "thread list should not contain NULL threads");
 
      // 从恢复动作列表中获取当前线程对应的动作(若不存在则用默认动作)
      const ResumeAction *const action =
          resume_actions.GetActionForThread(thread->GetID(), true);
      if (action == nullptr)
        continue;
 
      // 如果线程需要单步执行(eStateStepping)
      if (action->state == eStateStepping) {
        // 为线程设置软件单步(如修改指令指针、插入断点等)
        Status error = SetupSoftwareSingleStepping(
            static_cast<NativeThreadLinux &>(*thread));
        if (error.Fail())
          return error; // 若设置失败, 直接返回错误
      }
    }
  }
 
  // 遍历所有线程, 执行对应的恢复动作
  for (const auto &thread : m_threads) {
    assert(thread && "thread list should not contain NULL threads");
 
    // 获取当前线程的恢复动作
    const ResumeAction *const action =
        resume_actions.GetActionForThread(thread->GetID(), true);
 
    // 若没有为该线程指定动作, 跳过(保持当前状态)
    if (action == nullptr) {
      LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(),
               thread->GetID());
      continue;
    }
 
    // 记录当前处理的线程及其恢复状态
    LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}",
             action->state, GetID(), thread->GetID());
 
    // 根据恢复动作的状态(如运行、单步、暂停)执行不同操作
    switch (action->state) {
    case eStateRunning:    // 继续运行线程
    case eStateStepping: { // 单步执行线程
      // 获取需要传递给线程的信号(如断点信号需要在恢复时处理)
      const int signo = action->signal;
      // 调用 ResumeThread 实际恢复线程执行, 传入目标状态和信号
      Status error = ResumeThread(static_cast<NativeThreadLinux &>(*thread),
                                  action->state, signo);
      if (error.Fail()) {
        // 若恢复失败, 返回包含详细信息的错误
        return Status("NativeProcessLinux::%s: failed to resume thread "
                      "for pid %" PRIu64 ", tid %" PRIu64 ", error = %s",
                      __FUNCTION__, GetID(), thread->GetID(),
                      error.AsCString());
      }
      break;
    }
 
    case eStateSuspended:  // 线程保持暂停状态
    case eStateStopped:    // 线程保持停止状态
      // 无需操作, 线程继续保持当前状态
      break;
 
    default:
      // 处理未知状态, 返回错误
      return Status("NativeProcessLinux::%s (): unexpected state %s specified "
                    "for pid %" PRIu64 ", tid %" PRIu64,
                    __FUNCTION__, StateAsCString(action->state), GetID(),
                    thread->GetID());
    }
  }
 
  // 所有线程的恢复动作处理完成, 返回成功状态
  return Status();
}
/// \param[in] resume_actions 包含每个线程的恢复动作列表(状态和信号)
/// \return 成功返回空状态, 失败返回错误信息
Status NativeProcessLinux::Resume(const ResumeActionList &resume_actions) {
  // 获取日志对象, 用于调试日志输出(POSIX 进程相关日志)
  Log *log = GetLog(POSIXLog::Process);
  // 记录当前进程 ID 到日志
  LLDB_LOG(log, "pid {0}", GetID());
 
  // 通知跟踪器(如 Intel PT)进程即将恢复执行
  NotifyTracersProcessWillResume();
 
  // 检查当前平台是否支持硬件单步执行, 不支持则使用软件单步
  bool software_single_step = !SupportHardwareSingleStepping();
 
  // 如果需要使用软件单步执行
  if (software_single_step) {
    // 遍历所有线程, 为需要单步的线程设置软件单步环境
    for (const auto &thread : m_threads) {
      assert(thread && "thread list should not contain NULL threads");
 
      // 从恢复动作列表中获取当前线程对应的动作(若不存在则用默认动作)
      const ResumeAction *const action =
          resume_actions.GetActionForThread(thread->GetID(), true);
      if (action == nullptr)
        continue;
 
      // 如果线程需要单步执行(eStateStepping)
      if (action->state == eStateStepping) {
        // 为线程设置软件单步(如修改指令指针、插入断点等)
        Status error = SetupSoftwareSingleStepping(
            static_cast<NativeThreadLinux &>(*thread));
        if (error.Fail())
          return error; // 若设置失败, 直接返回错误
      }
    }
  }
 
  // 遍历所有线程, 执行对应的恢复动作
  for (const auto &thread : m_threads) {
    assert(thread && "thread list should not contain NULL threads");
 
    // 获取当前线程的恢复动作
    const ResumeAction *const action =
        resume_actions.GetActionForThread(thread->GetID(), true);
 
    // 若没有为该线程指定动作, 跳过(保持当前状态)
    if (action == nullptr) {
      LLDB_LOG(log, "no action specified for pid {0} tid {1}", GetID(),
               thread->GetID());
      continue;
    }
 
    // 记录当前处理的线程及其恢复状态
    LLDB_LOG(log, "processing resume action state {0} for pid {1} tid {2}",
             action->state, GetID(), thread->GetID());
 
    // 根据恢复动作的状态(如运行、单步、暂停)执行不同操作
    switch (action->state) {
    case eStateRunning:    // 继续运行线程
    case eStateStepping: { // 单步执行线程
      // 获取需要传递给线程的信号(如断点信号需要在恢复时处理)
      const int signo = action->signal;
      // 调用 ResumeThread 实际恢复线程执行, 传入目标状态和信号
      Status error = ResumeThread(static_cast<NativeThreadLinux &>(*thread),
                                  action->state, signo);
      if (error.Fail()) {
        // 若恢复失败, 返回包含详细信息的错误
        return Status("NativeProcessLinux::%s: failed to resume thread "
                      "for pid %" PRIu64 ", tid %" PRIu64 ", error = %s",
                      __FUNCTION__, GetID(), thread->GetID(),
                      error.AsCString());
      }
      break;
    }
 
    case eStateSuspended:  // 线程保持暂停状态
    case eStateStopped:    // 线程保持停止状态
      // 无需操作, 线程继续保持当前状态
      break;
 
    default:
      // 处理未知状态, 返回错误
      return Status("NativeProcessLinux::%s (): unexpected state %s specified "
                    "for pid %" PRIu64 ", tid %" PRIu64,
                    __FUNCTION__, StateAsCString(action->state), GetID(),
                    thread->GetID());
    }
  }
 
  // 所有线程的恢复动作处理完成, 返回成功状态
  return Status();
}
/// 判断当前架构是否支持硬件单步执行
/// 硬件单步执行通常通过CPU的调试寄存器实现, 比软件单步更高效
/// \return 若支持硬件单步返回true, 否则返回false
bool NativeProcessLinux::SupportHardwareSingleStepping() const {
  // 判断当前进程的架构是否为以下不支持硬件单步的架构:
  // - MIPS架构
  // - ARM架构(llvm::Triple::arm对应传统ARM架构)
  // - RISC-V架构
  // - 龙芯架构(LoongArch)
  if (m_arch.IsMIPS() || m_arch.GetMachine() == llvm::Triple::arm ||
      m_arch.GetTriple().isRISCV() || m_arch.GetTriple().isLoongArch())
    return false// 上述架构不支持硬件单步
   
  // 其他架构(如x86、x86_64、AArch64等)默认支持硬件单步
  return true;
}
/// 判断当前架构是否支持硬件单步执行
/// 硬件单步执行通常通过CPU的调试寄存器实现, 比软件单步更高效
/// \return 若支持硬件单步返回true, 否则返回false
bool NativeProcessLinux::SupportHardwareSingleStepping() const {
  // 判断当前进程的架构是否为以下不支持硬件单步的架构:
  // - MIPS架构
  // - ARM架构(llvm::Triple::arm对应传统ARM架构)
  // - RISC-V架构
  // - 龙芯架构(LoongArch)
  if (m_arch.IsMIPS() || m_arch.GetMachine() == llvm::Triple::arm ||
      m_arch.GetTriple().isRISCV() || m_arch.GetTriple().isLoongArch())
    return false// 上述架构不支持硬件单步
   
  // 其他架构(如x86、x86_64、AArch64等)默认支持硬件单步
  return true;
}
enum ArchType {
  UnknownArch,                ///< 未知架构
 
  arm,                        ///< ARM架构(小端模式):包括arm、armv.*、xscale等系列
  armeb,                      ///< ARM架构(大端模式):armeb
  aarch64,                    ///< AArch64架构(小端模式):64位ARM架构
  aarch64_be,                 ///< AArch64架构(大端模式)
  aarch64_32,                 ///< AArch64架构(小端模式, ILP32数据模型):32位应用运行在64位AArch64上
  arc,                        ///< ARC架构:Synopsys公司的ARC处理器
  avr,                        ///< AVR架构:Atmel公司的AVR微控制器
  bpfel,                      ///< eBPF/扩展BPF(小端模式):64位BPF虚拟机
  bpfeb,                      ///< eBPF/扩展BPF(大端模式)
  csky,                       ///< CSKY架构:中国平头哥的玄铁处理器
  dxil,                       ///< DXIL:32位DirectX字节码架构
  hexagon,                    ///< Hexagon架构:高通公司的Hexagon处理器
  loongarch32,                ///< 龙芯架构(32位):loongarch32
  loongarch64,                ///< 龙芯架构(64位):loongarch64
  m68k,                       ///< M68k架构:摩托罗拉680x0系列处理器
  mips,                       ///< MIPS架构(大端):包括mips、mipsallegrex、mipsr6等
  mipsel,                     ///< MIPS架构(小端):mipsel、mipsallegrexe、mipsr6el等
  mips64,                     ///< MIPS64架构(大端):mips64、mips64r6、mipsn32等
  mips64el,                   ///< MIPS64架构(小端):mips64el、mips64r6el等
  msp430,                     ///< MSP430架构:德州仪器的MSP430微控制器
  ppc,                        ///< PPC架构:PowerPC(32位, 大端)
  ppcle,                      ///< PPCLE架构:PowerPC(32位, 小端)
  ppc64,                      ///< PPC64架构:64位PowerPC(大端), 包括ppu
  ppc64le,                    ///< PPC64LE架构:64位PowerPC(小端)
  r600,                       ///< R600架构:AMD的HD2XXX到HD6XXX系列GPU
  amdgcn,                     ///< AMDGCN架构:AMD的GCN系列GPU
  riscv32,                    ///< RISC-V架构(32位)
  riscv64,                    ///< RISC-V架构(64位)
  sparc,                      ///< Sparc架构(32位, 大端)
  sparcv9,                    ///< Sparcv9架构(64位Sparc)
  sparcel,                    ///< Sparc架构(小端), 注意与Sparcle CPU变体区分
  systemz,                    ///< SystemZ架构:IBM的s390x大型机架构
  tce,                        ///< TCE架构:基于Transport Triggered Architecture的处理器
  tcele,                      ///< TCE架构(小端模式)
  thumb,                      ///< Thumb架构(小端):ARM的Thumb指令集, 包括thumbv.*系列
  thumbeb,                    ///< Thumb架构(大端)
  x86,                        ///< X86架构:32位x86处理器, 如i386到i986
  x86_64,                     ///< X86-64架构:64位x86处理器, 如amd64、x86_64
  xcore,                      ///< XCore架构:XMOS公司的多核处理器
  xtensa,                     ///< Xtensa架构:Tensilica公司的可配置处理器
  nvptx,                      ///< NVPTX架构:NVIDIA GPU的32位虚拟指令集
  nvptx64,                    ///< NVPTX64架构:NVIDIA GPU的64位虚拟指令集
  le32,                       ///< le32架构:通用32位小端CPU(如PNaCl)
  le64,                       ///< le64架构:通用64位小端CPU(如PNaCl)
  amdil,                      ///< AMDIL架构:AMD的中间语言(32位)
  amdil64,                    ///< AMDIL64架构:64位指针的AMDIL
  hsail,                      ///< HSAIL架构:AMD的异构系统架构中间语言(32位)
  hsail64,                    ///< HSAIL64架构:64位指针的HSAIL
  spir,                       ///< SPIR架构:OpenCL的32位标准可移植中间表示
  spir64,                     ///< SPIR64架构:OpenCL的64位标准可移植中间表示
  spirv32,                    ///< SPIR-V架构(32位指针):Vulkan/OpenCL的中间语言
  spirv64,                    ///< SPIR-V架构(64位指针)
  kalimba,                    ///< Kalimba架构:通用Kalimba处理器(常用于音频处理)
  shave,                      ///< SHAVE架构:Movidius的向量VLIW处理器
  lanai,                      ///< Lanai架构:32位Lanai处理器
  wasm32,                     ///< WebAssembly架构(32位指针)
  wasm64,                     ///< WebAssembly架构(64位指针)
  renderscript32,             ///< 32位RenderScript:Android的并行计算框架
  renderscript64,             ///< 64位RenderScript
  ve,                         ///< VE架构:NEC的SX-Aurora向量引擎
  LastArchType = ve           ///< 枚举值的结束标记, 用于边界检查
};
enum ArchType {
  UnknownArch,                ///< 未知架构
 
  arm,                        ///< ARM架构(小端模式):包括arm、armv.*、xscale等系列
  armeb,                      ///< ARM架构(大端模式):armeb
  aarch64,                    ///< AArch64架构(小端模式):64位ARM架构
  aarch64_be,                 ///< AArch64架构(大端模式)
  aarch64_32,                 ///< AArch64架构(小端模式, ILP32数据模型):32位应用运行在64位AArch64上
  arc,                        ///< ARC架构:Synopsys公司的ARC处理器
  avr,                        ///< AVR架构:Atmel公司的AVR微控制器
  bpfel,                      ///< eBPF/扩展BPF(小端模式):64位BPF虚拟机
  bpfeb,                      ///< eBPF/扩展BPF(大端模式)
  csky,                       ///< CSKY架构:中国平头哥的玄铁处理器
  dxil,                       ///< DXIL:32位DirectX字节码架构
  hexagon,                    ///< Hexagon架构:高通公司的Hexagon处理器
  loongarch32,                ///< 龙芯架构(32位):loongarch32
  loongarch64,                ///< 龙芯架构(64位):loongarch64
  m68k,                       ///< M68k架构:摩托罗拉680x0系列处理器
  mips,                       ///< MIPS架构(大端):包括mips、mipsallegrex、mipsr6等
  mipsel,                     ///< MIPS架构(小端):mipsel、mipsallegrexe、mipsr6el等
  mips64,                     ///< MIPS64架构(大端):mips64、mips64r6、mipsn32等
  mips64el,                   ///< MIPS64架构(小端):mips64el、mips64r6el等
  msp430,                     ///< MSP430架构:德州仪器的MSP430微控制器
  ppc,                        ///< PPC架构:PowerPC(32位, 大端)
  ppcle,                      ///< PPCLE架构:PowerPC(32位, 小端)
  ppc64,                      ///< PPC64架构:64位PowerPC(大端), 包括ppu
  ppc64le,                    ///< PPC64LE架构:64位PowerPC(小端)
  r600,                       ///< R600架构:AMD的HD2XXX到HD6XXX系列GPU
  amdgcn,                     ///< AMDGCN架构:AMD的GCN系列GPU
  riscv32,                    ///< RISC-V架构(32位)
  riscv64,                    ///< RISC-V架构(64位)
  sparc,                      ///< Sparc架构(32位, 大端)
  sparcv9,                    ///< Sparcv9架构(64位Sparc)
  sparcel,                    ///< Sparc架构(小端), 注意与Sparcle CPU变体区分
  systemz,                    ///< SystemZ架构:IBM的s390x大型机架构
  tce,                        ///< TCE架构:基于Transport Triggered Architecture的处理器
  tcele,                      ///< TCE架构(小端模式)
  thumb,                      ///< Thumb架构(小端):ARM的Thumb指令集, 包括thumbv.*系列
  thumbeb,                    ///< Thumb架构(大端)
  x86,                        ///< X86架构:32位x86处理器, 如i386到i986
  x86_64,                     ///< X86-64架构:64位x86处理器, 如amd64、x86_64
  xcore,                      ///< XCore架构:XMOS公司的多核处理器
  xtensa,                     ///< Xtensa架构:Tensilica公司的可配置处理器
  nvptx,                      ///< NVPTX架构:NVIDIA GPU的32位虚拟指令集
  nvptx64,                    ///< NVPTX64架构:NVIDIA GPU的64位虚拟指令集
  le32,                       ///< le32架构:通用32位小端CPU(如PNaCl)
  le64,                       ///< le64架构:通用64位小端CPU(如PNaCl)
  amdil,                      ///< AMDIL架构:AMD的中间语言(32位)
  amdil64,                    ///< AMDIL64架构:64位指针的AMDIL
  hsail,                      ///< HSAIL架构:AMD的异构系统架构中间语言(32位)
  hsail64,                    ///< HSAIL64架构:64位指针的HSAIL
  spir,                       ///< SPIR架构:OpenCL的32位标准可移植中间表示
  spir64,                     ///< SPIR64架构:OpenCL的64位标准可移植中间表示
  spirv32,                    ///< SPIR-V架构(32位指针):Vulkan/OpenCL的中间语言
  spirv64,                    ///< SPIR-V架构(64位指针)
  kalimba,                    ///< Kalimba架构:通用Kalimba处理器(常用于音频处理)
  shave,                      ///< SHAVE架构:Movidius的向量VLIW处理器
  lanai,                      ///< Lanai架构:32位Lanai处理器
  wasm32,                     ///< WebAssembly架构(32位指针)
  wasm64,                     ///< WebAssembly架构(64位指针)
  renderscript32,             ///< 32位RenderScript:Android的并行计算框架
  renderscript64,             ///< 64位RenderScript
  ve,                         ///< VE架构:NEC的SX-Aurora向量引擎
  LastArchType = ve           ///< 枚举值的结束标记, 用于边界检查
};
/// \param[in] thread 需要设置软件单步的线程
/// \return 成功返回空状态, 失败返回包含错误信息的状态
Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
    NativeThreadProtocol &thread) {
  Status error;  // 存储操作结果状态
  // 获取线程所属的进程、线程的寄存器上下文、进程的架构信息
  NativeProcessProtocol &process = thread.GetProcess();
  NativeRegisterContext &register_context = thread.GetRegisterContext();
  const ArchSpec &arch = process.GetArchitecture();
 
  // 为当前架构创建指令模拟器(用于解析指令、预测下一条指令地址)
  // 模拟器类型指定为“修改PC的指令”(eInstructionTypePCModifying), 聚焦影响程序计数器的指令
  std::unique_ptr<EmulateInstruction> emulator_up(
      EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
                                     nullptr));
 
  // 若未找到对应架构的指令模拟器, 返回错误(无法预测下一条指令地址, 无法设置软件单步)
  if (emulator_up == nullptr)
    return Status("Instruction emulator not found!");
 
  // 创建模拟器的“数据载体”(Baton), 用于传递进程、寄存器上下文给模拟器回调函数
  EmulatorBaton baton(process, register_context);
  // 将Baton绑定到模拟器, 供后续回调函数访问进程/寄存器信息
  emulator_up->SetBaton(&baton);
  // 设置模拟器的内存/寄存器读写回调函数(模拟器需通过这些回调获取真实进程的内存和寄存器数据)
  emulator_up->SetReadMemCallback(&ReadMemoryCallback);    // 读内存回调
  emulator_up->SetReadRegCallback(&ReadRegisterCallback);  // 读寄存器回调
  emulator_up->SetWriteMemCallback(&WriteMemoryCallback);  // 写内存回调
  emulator_up->SetWriteRegCallback(&WriteRegisterCallback); // 写寄存器回调
 
  // 读取当前线程PC指向的指令(从被调试进程内存中读取)
  if (!emulator_up->ReadInstruction())
    return Status("Read instruction failed!");
 
  // 模拟执行当前指令, 自动更新PC(预测执行完该指令后的下一个PC值)
  // eEmulateInstructionOptionAutoAdvancePC:让模拟器自动计算并更新PC
  bool emulation_result =
      emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
 
  // 获取通用寄存器中“程序计数器(PC)”和“标志寄存器(Flags)”的信息
  const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);  // 通用PC寄存器
  const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); // 通用标志寄存器(如x86的EFLAGS)
 
  // 从Baton中查找模拟后PC和Flags的数值(Baton在回调中记录了模拟器修改的寄存器值)
  // 键值为寄存器的DWARF编号(调试标准中定义的寄存器唯一标识)
  auto pc_it =
      baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
  auto flags_it = reg_info_flags == nullptr
                      ? baton.m_register_values.end() // 若架构无Flags寄存器, 直接设为end
                      : baton.m_register_values.find(
                            reg_info_flags->kinds[eRegisterKindDWARF]);
 
  lldb::addr_t next_pc;    // 执行完当前指令后的下一个PC地址(软件断点要插入的位置)
  lldb::addr_t next_flags; // 执行完当前指令后的Flags值(用于判断架构模式, 如ARM/Thumb)
 
  // 根据指令模拟结果, 计算next_pc和next_flags
  if (emulation_result) {
    // 模拟成功:说明模拟器已正确预测下一个PC
    assert(pc_it != baton.m_register_values.end() &&
           "Emulation was successfull but PC wasn't updated");
    next_pc = pc_it->second.GetAsUInt64(); // 从Baton中获取模拟后的PC值
 
    // 获取模拟后的Flags值(若存在Flags寄存器), 否则直接读取当前Flags
    if (flags_it != baton.m_register_values.end())
      next_flags = flags_it->second.GetAsUInt64();
    else
      next_flags = ReadFlags(register_context);
  } else if (pc_it == baton.m_register_values.end()) {
    // 模拟失败, 但未修改PC:说明当前指令不影响PC(如普通运算指令)
    // 此时直接用“当前PC + 指令长度”作为下一个PC(简单递增)
    next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize();
    next_flags = ReadFlags(register_context); // 直接读取当前Flags
  } else {
    // 模拟失败, 但已修改PC:属于未知错误(影响PC的指令模拟失败, 无法确定下一个PC)
    return Status("Instruction emulation failed unexpectedly.");
  }
 
  // 根据架构类型, 确定软件断点的“大小提示”(不同架构的指令长度不同, 断点需覆盖完整指令)
  int size_hint = 0;
  if (arch.GetMachine() == llvm::Triple::arm) {
    // ARM架构:需区分ARM模式(4字节指令)和Thumb模式(2字节指令)
    // Flags的第5位(0x20)表示Thumb模式(ARM架构标准)
    if (next_flags & 0x20) {
      size_hint = 2; // Thumb模式:断点大小2字节
    } else {
      size_hint = 4; // ARM模式:断点大小4字节
    }
  } else if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
             arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch()) {
    // 这些架构的指令长度固定为4字节, 断点大小设为4
    size_hint = 4;
  }
 
  // 在预测的next_pc地址插入软件断点(hardware=false表示软件断点)
  error = process.SetBreakpoint(next_pc, size_hint, /*hardware=*/false);
 
  // 特殊错误处理:若断点插入失败是因为next_pc超出地址空间(如非法地址)
  // 此时忽略错误, 让被调试进程触发段错误(由调试器后续处理)
  if (error.GetError() == EIO || error.GetError() == EFAULT) {
    return Status();
  } else if (error.Fail()) {
    // 其他断点插入失败(如权限不足), 返回错误
    return error;
  }
 
  // 记录“正在软件单步的线程”:键为线程ID, 值为下一个PC(后续断点触发后需移除该断点)
  m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
 
  // 软件单步设置完成
  return Status();
}
/// \param[in] thread 需要设置软件单步的线程
/// \return 成功返回空状态, 失败返回包含错误信息的状态
Status NativeProcessSoftwareSingleStep::SetupSoftwareSingleStepping(
    NativeThreadProtocol &thread) {
  Status error;  // 存储操作结果状态
  // 获取线程所属的进程、线程的寄存器上下文、进程的架构信息
  NativeProcessProtocol &process = thread.GetProcess();
  NativeRegisterContext &register_context = thread.GetRegisterContext();
  const ArchSpec &arch = process.GetArchitecture();
 
  // 为当前架构创建指令模拟器(用于解析指令、预测下一条指令地址)
  // 模拟器类型指定为“修改PC的指令”(eInstructionTypePCModifying), 聚焦影响程序计数器的指令
  std::unique_ptr<EmulateInstruction> emulator_up(
      EmulateInstruction::FindPlugin(arch, eInstructionTypePCModifying,
                                     nullptr));
 
  // 若未找到对应架构的指令模拟器, 返回错误(无法预测下一条指令地址, 无法设置软件单步)
  if (emulator_up == nullptr)
    return Status("Instruction emulator not found!");
 
  // 创建模拟器的“数据载体”(Baton), 用于传递进程、寄存器上下文给模拟器回调函数
  EmulatorBaton baton(process, register_context);
  // 将Baton绑定到模拟器, 供后续回调函数访问进程/寄存器信息
  emulator_up->SetBaton(&baton);
  // 设置模拟器的内存/寄存器读写回调函数(模拟器需通过这些回调获取真实进程的内存和寄存器数据)
  emulator_up->SetReadMemCallback(&ReadMemoryCallback);    // 读内存回调
  emulator_up->SetReadRegCallback(&ReadRegisterCallback);  // 读寄存器回调
  emulator_up->SetWriteMemCallback(&WriteMemoryCallback);  // 写内存回调
  emulator_up->SetWriteRegCallback(&WriteRegisterCallback); // 写寄存器回调
 
  // 读取当前线程PC指向的指令(从被调试进程内存中读取)
  if (!emulator_up->ReadInstruction())
    return Status("Read instruction failed!");
 
  // 模拟执行当前指令, 自动更新PC(预测执行完该指令后的下一个PC值)
  // eEmulateInstructionOptionAutoAdvancePC:让模拟器自动计算并更新PC
  bool emulation_result =
      emulator_up->EvaluateInstruction(eEmulateInstructionOptionAutoAdvancePC);
 
  // 获取通用寄存器中“程序计数器(PC)”和“标志寄存器(Flags)”的信息
  const RegisterInfo *reg_info_pc = register_context.GetRegisterInfo(
      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);  // 通用PC寄存器
  const RegisterInfo *reg_info_flags = register_context.GetRegisterInfo(
      eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS); // 通用标志寄存器(如x86的EFLAGS)
 
  // 从Baton中查找模拟后PC和Flags的数值(Baton在回调中记录了模拟器修改的寄存器值)
  // 键值为寄存器的DWARF编号(调试标准中定义的寄存器唯一标识)
  auto pc_it =
      baton.m_register_values.find(reg_info_pc->kinds[eRegisterKindDWARF]);
  auto flags_it = reg_info_flags == nullptr
                      ? baton.m_register_values.end() // 若架构无Flags寄存器, 直接设为end
                      : baton.m_register_values.find(
                            reg_info_flags->kinds[eRegisterKindDWARF]);
 
  lldb::addr_t next_pc;    // 执行完当前指令后的下一个PC地址(软件断点要插入的位置)
  lldb::addr_t next_flags; // 执行完当前指令后的Flags值(用于判断架构模式, 如ARM/Thumb)
 
  // 根据指令模拟结果, 计算next_pc和next_flags
  if (emulation_result) {
    // 模拟成功:说明模拟器已正确预测下一个PC
    assert(pc_it != baton.m_register_values.end() &&
           "Emulation was successfull but PC wasn't updated");
    next_pc = pc_it->second.GetAsUInt64(); // 从Baton中获取模拟后的PC值
 
    // 获取模拟后的Flags值(若存在Flags寄存器), 否则直接读取当前Flags
    if (flags_it != baton.m_register_values.end())
      next_flags = flags_it->second.GetAsUInt64();
    else
      next_flags = ReadFlags(register_context);
  } else if (pc_it == baton.m_register_values.end()) {
    // 模拟失败, 但未修改PC:说明当前指令不影响PC(如普通运算指令)
    // 此时直接用“当前PC + 指令长度”作为下一个PC(简单递增)
    next_pc = register_context.GetPC() + emulator_up->GetOpcode().GetByteSize();
    next_flags = ReadFlags(register_context); // 直接读取当前Flags
  } else {
    // 模拟失败, 但已修改PC:属于未知错误(影响PC的指令模拟失败, 无法确定下一个PC)
    return Status("Instruction emulation failed unexpectedly.");
  }
 
  // 根据架构类型, 确定软件断点的“大小提示”(不同架构的指令长度不同, 断点需覆盖完整指令)
  int size_hint = 0;
  if (arch.GetMachine() == llvm::Triple::arm) {
    // ARM架构:需区分ARM模式(4字节指令)和Thumb模式(2字节指令)
    // Flags的第5位(0x20)表示Thumb模式(ARM架构标准)
    if (next_flags & 0x20) {
      size_hint = 2; // Thumb模式:断点大小2字节
    } else {
      size_hint = 4; // ARM模式:断点大小4字节
    }
  } else if (arch.IsMIPS() || arch.GetTriple().isPPC64() ||
             arch.GetTriple().isRISCV() || arch.GetTriple().isLoongArch()) {
    // 这些架构的指令长度固定为4字节, 断点大小设为4
    size_hint = 4;
  }
 
  // 在预测的next_pc地址插入软件断点(hardware=false表示软件断点)
  error = process.SetBreakpoint(next_pc, size_hint, /*hardware=*/false);
 
  // 特殊错误处理:若断点插入失败是因为next_pc超出地址空间(如非法地址)
  // 此时忽略错误, 让被调试进程触发段错误(由调试器后续处理)
  if (error.GetError() == EIO || error.GetError() == EFAULT) {
    return Status();
  } else if (error.Fail()) {
    // 其他断点插入失败(如权限不足), 返回错误
    return error;
  }
 
  // 记录“正在软件单步的线程”:键为线程ID, 值为下一个PC(后续断点触发后需移除该断点)
  m_threads_stepping_with_breakpoint.insert({thread.GetID(), next_pc});
 
  // 软件单步设置完成
  return Status();
}
static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
                                 const EmulateInstruction::Context &context,
                                 lldb::addr_t addr, void *dst, size_t length) {
  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
 
  size_t bytes_read;
  emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read);
  return bytes_read;
}
static size_t ReadMemoryCallback(EmulateInstruction *instruction, void *baton,
                                 const EmulateInstruction::Context &context,
                                 lldb::addr_t addr, void *dst, size_t length) {
  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
 
  size_t bytes_read;
  emulator_baton->m_process.ReadMemory(addr, dst, length, bytes_read);
  return bytes_read;
}
/// \param [in] addr 要读取的内存地址
/// \param [in] buf 存储读取结果的缓冲区
/// \param [in] size 要读取的字节数
/// \param [out] bytes_read 实际读取的字节数
/// \return 成功返回空状态, 失败返回包含错误信息的状态
Status NativeProcessLinux::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
                                      size_t &bytes_read) {
  // 检查是否支持process_vm_readv系统调用(更高效的内存读取方式)
  if (ProcessVmReadvSupported()) {
    // process_vm_readv路径比ptrace API快约50倍, 若支持则优先使用
 
    // 定义本地和远程的iovec结构(用于描述数据缓冲区)
    struct iovec local_iov, remote_iov;
    local_iov.iov_base = buf;         // 本地缓冲区地址(存储读取结果)
    local_iov.iov_len = size;         // 本地缓冲区长度
    remote_iov.iov_base = reinterpret_cast<void *>(addr);  // 远程进程地址(要读取的地址)
    remote_iov.iov_len = size;        // 要读取的长度
 
    // 调用process_vm_readv系统调用读取内存
    bytes_read = process_vm_readv(GetCurrentThreadID(), &local_iov, 1,
                                  &remote_iov, 1, 0);
    // 判断是否成功读取了请求的所有字节
    const bool success = bytes_read == size;
 
    // 获取日志对象并记录操作信息
    Log *log = GetLog(POSIXLog::Process);
    LLDB_LOG(log,
             "using process_vm_readv to read {0} bytes from inferior "
             "address {1:x}: {2}",
             size, addr, success ? "Success" : llvm::sys::StrError(errno));
 
    // 若成功读取所有字节, 返回成功状态
    if (success)
      return Status();
    // 若失败, 则回退使用ptrace API重试
  }
 
  // 以下为使用ptrace API读取内存的逻辑
  unsigned char *dst = static_cast<unsigned char *>(buf);  // 目标缓冲区指针
  size_t remainder;  // 剩余需要读取的字节数
  long data;         // 存储从ptrace读取的字数据
 
  // 获取内存操作相关的日志对象
  Log *log = GetLog(POSIXLog::Memory);
  LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
 
  // 循环读取所有请求的字节
  for (bytes_read = 0; bytes_read < size; bytes_read += remainder) {
    // 调用ptrace包装函数读取一个字的数据
    Status error = NativeProcessLinux::PtraceWrapper(
        PTRACE_PEEKDATA, GetCurrentThreadID(), (void *)addr, nullptr, 0, &data);
    // 若读取失败, 返回错误状态
    if (error.Fail())
      return error;
 
    // 计算剩余需要读取的字节数
    remainder = size - bytes_read;
    // 每次最多读取一个字的大小(k_ptrace_word_size)
    remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
 
    // 将读取到的数据复制到目标缓冲区
    memcpy(dst, &data, remainder);
 
    // 记录读取的内存地址和数据
    LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data);
    // 移动地址指针和缓冲区指针到下一个字
    addr += k_ptrace_word_size;
    dst += k_ptrace_word_size;
  }
  // 全部读取完成, 返回成功状态
  return Status();
}
/// \param [in] addr 要读取的内存地址
/// \param [in] buf 存储读取结果的缓冲区
/// \param [in] size 要读取的字节数
/// \param [out] bytes_read 实际读取的字节数
/// \return 成功返回空状态, 失败返回包含错误信息的状态
Status NativeProcessLinux::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
                                      size_t &bytes_read) {
  // 检查是否支持process_vm_readv系统调用(更高效的内存读取方式)
  if (ProcessVmReadvSupported()) {
    // process_vm_readv路径比ptrace API快约50倍, 若支持则优先使用
 
    // 定义本地和远程的iovec结构(用于描述数据缓冲区)
    struct iovec local_iov, remote_iov;
    local_iov.iov_base = buf;         // 本地缓冲区地址(存储读取结果)
    local_iov.iov_len = size;         // 本地缓冲区长度
    remote_iov.iov_base = reinterpret_cast<void *>(addr);  // 远程进程地址(要读取的地址)
    remote_iov.iov_len = size;        // 要读取的长度
 
    // 调用process_vm_readv系统调用读取内存
    bytes_read = process_vm_readv(GetCurrentThreadID(), &local_iov, 1,
                                  &remote_iov, 1, 0);
    // 判断是否成功读取了请求的所有字节
    const bool success = bytes_read == size;
 
    // 获取日志对象并记录操作信息
    Log *log = GetLog(POSIXLog::Process);
    LLDB_LOG(log,
             "using process_vm_readv to read {0} bytes from inferior "
             "address {1:x}: {2}",
             size, addr, success ? "Success" : llvm::sys::StrError(errno));
 
    // 若成功读取所有字节, 返回成功状态
    if (success)
      return Status();
    // 若失败, 则回退使用ptrace API重试
  }
 
  // 以下为使用ptrace API读取内存的逻辑
  unsigned char *dst = static_cast<unsigned char *>(buf);  // 目标缓冲区指针
  size_t remainder;  // 剩余需要读取的字节数
  long data;         // 存储从ptrace读取的字数据
 
  // 获取内存操作相关的日志对象
  Log *log = GetLog(POSIXLog::Memory);
  LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
 
  // 循环读取所有请求的字节
  for (bytes_read = 0; bytes_read < size; bytes_read += remainder) {
    // 调用ptrace包装函数读取一个字的数据
    Status error = NativeProcessLinux::PtraceWrapper(
        PTRACE_PEEKDATA, GetCurrentThreadID(), (void *)addr, nullptr, 0, &data);
    // 若读取失败, 返回错误状态
    if (error.Fail())
      return error;
 
    // 计算剩余需要读取的字节数
    remainder = size - bytes_read;
    // 每次最多读取一个字的大小(k_ptrace_word_size)
    remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
 
    // 将读取到的数据复制到目标缓冲区
    memcpy(dst, &data, remainder);
 
    // 记录读取的内存地址和数据
    LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data);
    // 移动地址指针和缓冲区指针到下一个字
    addr += k_ptrace_word_size;
    dst += k_ptrace_word_size;
  }
  // 全部读取完成, 返回成功状态
  return Status();
}
// 向Linux系统中的目标进程内存写入数据
// 参数:
// - addr: 目标内存地址(要写入数据的起始地址)
// - buf: 指向源数据缓冲区的指针(待写入的数据)
// - size: 要写入的数据大小(字节数)
// - bytes_written: 输出参数, 实际成功写入的字节数
// 返回值:操作状态(成功或失败信息)
Status NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf,
                                       size_t size, size_t &bytes_written) {
  // 将源数据缓冲区指针转换为unsigned char*, 方便按字节操作
  const unsigned char *src = static_cast<const unsigned char *>(buf);
  size_t remainder;  // 记录每次循环中待写入的剩余字节数
  Status error;      // 用于存储操作状态
 
  // 获取日志对象, 用于记录内存操作相关日志
  Log *log = GetLog(POSIXLog::Memory);
  // 记录写入操作的起始信息(地址、缓冲区、大小)
  LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
 
  // 循环写入数据, 直到所有数据都被写入或发生错误
  // bytes_written记录已写入的总字节数, 初始为0
  for (bytes_written = 0; bytes_written < size; bytes_written += remainder) {
    // 计算当前剩余待写入的字节数
    remainder = size - bytes_written;
    // 每次写入的最大字节数不超过ptrace操作的字长(k_ptrace_word_size)
    remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
 
    // 当剩余字节数等于ptrace字长时, 直接进行完整字写入
    if (remainder == k_ptrace_word_size) {
      unsigned long data = 0;  // 存储要写入的字数据
      // 将源缓冲区中的数据复制到data变量(按ptrace字长)
      memcpy(&data, src, k_ptrace_word_size);
 
      // 记录当前写入的地址和数据(十六进制)
      LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data);
      // 调用ptrace的PTRACE_POKEDATA命令写入数据
      // 参数:命令类型、目标线程ID、目标地址、要写入的数据
      error = NativeProcessLinux::PtraceWrapper(
          PTRACE_POKEDATA, GetCurrentThreadID(), (void *)addr, (void *)data);
      // 如果写入失败, 返回错误状态
      if (error.Fail())
        return error;
    } else {
      // 当剩余字节数小于ptrace字长时, 需要先读取原有数据再部分修改
      unsigned char buff[8];  // 临时缓冲区, 用于存储读取到的原有数据
      size_t bytes_read;      // 实际读取的字节数
 
      // 先读取目标地址处的完整ptrace字长数据
      error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read);
      if (error.Fail())
        return error;
 
      // 将源数据中剩余的部分复制到临时缓冲区(覆盖原有数据的对应部分)
      memcpy(buff, src, remainder);
 
      // 将修改后的完整字数据写回目标地址
      size_t bytes_written_rec;
      error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec);
      if (error.Fail())
        return error;
 
      // 记录写入的地址、源数据和实际写入的数据(用于调试)
      LLDB_LOG(log, "[{0:x}]:{1:x} ({2:x})", addr, *(const unsigned long *)src,
               *(unsigned long *)buff);
    }
 
    // 移动地址指针和源数据指针, 准备下一次循环
    addr += k_ptrace_word_size;
    src += k_ptrace_word_size;
  }
 
  // 所有数据写入完成, 返回成功状态
  return error;
}
// 向Linux系统中的目标进程内存写入数据
// 参数:
// - addr: 目标内存地址(要写入数据的起始地址)
// - buf: 指向源数据缓冲区的指针(待写入的数据)
// - size: 要写入的数据大小(字节数)
// - bytes_written: 输出参数, 实际成功写入的字节数
// 返回值:操作状态(成功或失败信息)
Status NativeProcessLinux::WriteMemory(lldb::addr_t addr, const void *buf,
                                       size_t size, size_t &bytes_written) {
  // 将源数据缓冲区指针转换为unsigned char*, 方便按字节操作
  const unsigned char *src = static_cast<const unsigned char *>(buf);
  size_t remainder;  // 记录每次循环中待写入的剩余字节数
  Status error;      // 用于存储操作状态
 
  // 获取日志对象, 用于记录内存操作相关日志
  Log *log = GetLog(POSIXLog::Memory);
  // 记录写入操作的起始信息(地址、缓冲区、大小)
  LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
 
  // 循环写入数据, 直到所有数据都被写入或发生错误
  // bytes_written记录已写入的总字节数, 初始为0
  for (bytes_written = 0; bytes_written < size; bytes_written += remainder) {
    // 计算当前剩余待写入的字节数
    remainder = size - bytes_written;
    // 每次写入的最大字节数不超过ptrace操作的字长(k_ptrace_word_size)
    remainder = remainder > k_ptrace_word_size ? k_ptrace_word_size : remainder;
 
    // 当剩余字节数等于ptrace字长时, 直接进行完整字写入
    if (remainder == k_ptrace_word_size) {
      unsigned long data = 0;  // 存储要写入的字数据
      // 将源缓冲区中的数据复制到data变量(按ptrace字长)
      memcpy(&data, src, k_ptrace_word_size);
 
      // 记录当前写入的地址和数据(十六进制)
      LLDB_LOG(log, "[{0:x}]:{1:x}", addr, data);
      // 调用ptrace的PTRACE_POKEDATA命令写入数据
      // 参数:命令类型、目标线程ID、目标地址、要写入的数据
      error = NativeProcessLinux::PtraceWrapper(
          PTRACE_POKEDATA, GetCurrentThreadID(), (void *)addr, (void *)data);
      // 如果写入失败, 返回错误状态
      if (error.Fail())
        return error;
    } else {
      // 当剩余字节数小于ptrace字长时, 需要先读取原有数据再部分修改
      unsigned char buff[8];  // 临时缓冲区, 用于存储读取到的原有数据
      size_t bytes_read;      // 实际读取的字节数
 
      // 先读取目标地址处的完整ptrace字长数据
      error = ReadMemory(addr, buff, k_ptrace_word_size, bytes_read);
      if (error.Fail())
        return error;
 
      // 将源数据中剩余的部分复制到临时缓冲区(覆盖原有数据的对应部分)
      memcpy(buff, src, remainder);
 
      // 将修改后的完整字数据写回目标地址
      size_t bytes_written_rec;
      error = WriteMemory(addr, buff, k_ptrace_word_size, bytes_written_rec);
      if (error.Fail())
        return error;
 
      // 记录写入的地址、源数据和实际写入的数据(用于调试)
      LLDB_LOG(log, "[{0:x}]:{1:x} ({2:x})", addr, *(const unsigned long *)src,
               *(unsigned long *)buff);
    }
 
    // 移动地址指针和源数据指针, 准备下一次循环
    addr += k_ptrace_word_size;
    src += k_ptrace_word_size;
  }
 
  // 所有数据写入完成, 返回成功状态
  return error;
}
// 读取寄存器回调函数, 用于在指令模拟过程中获取寄存器值
// 参数说明:
// - instruction: 正在模拟的指令对象
// - baton: 传递的参数, 这里是EmulatorBaton类型的指针
// - reg_info: 要读取的寄存器信息
// - reg_value: 输出参数, 用于存储读取到的寄存器值
// 返回值:成功读取寄存器值返回true, 否则返回false
static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
                                 const RegisterInfo *reg_info,
                                 RegisterValue &reg_value) {
  // 将传递的通用指针转换为EmulatorBaton类型指针, 以便访问其中的数据
  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
 
  // 在寄存器值映射表中查找指定DWARF类型寄存器的值
  // 使用DWARF寄存器编号作为键进行查找
  auto it = emulator_baton->m_register_values.find(
      reg_info->kinds[eRegisterKindDWARF]);
  // 如果找到对应的寄存器值
  if (it != emulator_baton->m_register_values.end()) {
    // 将找到的值赋给输出参数
    reg_value = it->second;
    // 成功获取寄存器值, 返回true
    return true;
  }
 
  // 如果在映射表中未找到, 说明需要从寄存器上下文中获取
  // 模拟器仅填充DWARF寄存器编号(在某些情况下还有通用寄存器编号)
  // 基于DWARF寄存器编号从寄存器上下文获取完整的寄存器信息
  const RegisterInfo *full_reg_info =
      emulator_baton->m_reg_context.GetRegisterInfo(
          eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
 
  // 从寄存器上下文读取寄存器值
  Status error =
      emulator_baton->m_reg_context.ReadRegister(full_reg_info, reg_value);
  // 如果读取成功, 返回true
  if (error.Success())
    return true;
 
  // 所有尝试都失败, 返回false
  return false;
}
// 读取寄存器回调函数, 用于在指令模拟过程中获取寄存器值
// 参数说明:
// - instruction: 正在模拟的指令对象
// - baton: 传递的参数, 这里是EmulatorBaton类型的指针
// - reg_info: 要读取的寄存器信息
// - reg_value: 输出参数, 用于存储读取到的寄存器值
// 返回值:成功读取寄存器值返回true, 否则返回false
static bool ReadRegisterCallback(EmulateInstruction *instruction, void *baton,
                                 const RegisterInfo *reg_info,
                                 RegisterValue &reg_value) {
  // 将传递的通用指针转换为EmulatorBaton类型指针, 以便访问其中的数据
  EmulatorBaton *emulator_baton = static_cast<EmulatorBaton *>(baton);
 
  // 在寄存器值映射表中查找指定DWARF类型寄存器的值
  // 使用DWARF寄存器编号作为键进行查找
  auto it = emulator_baton->m_register_values.find(
      reg_info->kinds[eRegisterKindDWARF]);
  // 如果找到对应的寄存器值
  if (it != emulator_baton->m_register_values.end()) {
    // 将找到的值赋给输出参数
    reg_value = it->second;
    // 成功获取寄存器值, 返回true
    return true;
  }
 
  // 如果在映射表中未找到, 说明需要从寄存器上下文中获取
  // 模拟器仅填充DWARF寄存器编号(在某些情况下还有通用寄存器编号)
  // 基于DWARF寄存器编号从寄存器上下文获取完整的寄存器信息
  const RegisterInfo *full_reg_info =
      emulator_baton->m_reg_context.GetRegisterInfo(
          eRegisterKindDWARF, reg_info->kinds[eRegisterKindDWARF]);
 
  // 从寄存器上下文读取寄存器值
  Status error =
      emulator_baton->m_reg_context.ReadRegister(full_reg_info, reg_value);
  // 如果读取成功, 返回true
  if (error.Success())
    return true;
 
  // 所有尝试都失败, 返回false
  return false;
}
// 读取ARM64架构下指定寄存器的值
// 参数:
// - reg_info: 寄存器信息结构体, 包含寄存器的各种属性(如编号、偏移量等)
// - reg_value: 输出参数, 用于存储读取到的寄存器值
// 返回值:操作状态(成功或失败信息)
Status
NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
                                               RegisterValue &reg_value) {
  Status error;  // 用于存储操作状态的变量
 
  // 检查寄存器信息指针是否为空
  if (!reg_info) {
    error.SetErrorString("reg_info NULL");  // 设置错误信息
    return error;
  }
 
  // 获取LLDB内部使用的寄存器编号(不同于硬件或DWARF编号)
  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
 
  // 检查寄存器编号是否有效
  if (reg == LLDB_INVALID_REGNUM)
    return Status("no lldb regnum for %s",
                  reg_info && reg_info->name ? reg_info->name : "<unknown register>");
 
  // 声明用于存储数据来源的指针和相关变量
  uint8_t *src;                  // 指向寄存器数据的源地址
  uint32_t offset = LLDB_INVALID_INDEX32;  // 寄存器在缓冲区中的偏移量
  uint64_t sve_vg;               // SVE向量长度相关值
  std::vector<uint8_t> sve_reg_non_live;  // 用于存储非活跃SVE寄存器的临时缓冲区
 
  // 处理通用寄存器(GPR: General Purpose Registers)
  if (IsGPR(reg)) {
    error = ReadGPR();  // 读取所有通用寄存器到内部缓冲区
    if (error.Fail())   // 检查读取是否失败
      return error;
 
    // 获取该寄存器在GPR缓冲区中的字节偏移量
    offset = reg_info->byte_offset;
    // 断言确保偏移量在有效范围内(调试时检查用)
    assert(offset < GetGPRSize());
    // 计算源数据地址 = GPR缓冲区起始地址 + 偏移量
    src = (uint8_t *)GetGPRBuffer() + offset;
 
  // 处理浮点寄存器(FPR: Floating Point Registers)
  } else if (IsFPR(reg)) {
    // 检查SVE(Scalable Vector Extension)是否禁用
    if (m_sve_state == SVEState::Disabled) {
      // SVE禁用时, 使用传统方式访问FPU寄存器
      error = ReadFPR();  // 读取所有浮点寄存器到内部缓冲区
      if (error.Fail())
        return error;
 
      // 计算该浮点寄存器在FPR缓冲区中的偏移量
      offset = CalculateFprOffset(reg_info);
      assert(offset < GetFPRSize());
      src = (uint8_t *)GetFPRBuffer() + offset;
    } else {
      // SVE启用时, 读取并缓存SVE相关的ptrace数据
      error = ReadAllSVE();
      if (error.Fail())
        return error;
 
      // FPSR(浮点状态寄存器)和FPCR(浮点控制寄存器)的位置因SVE状态而异:
      // - 在SVEState::FPSIMD状态下, 位于Z寄存器之后
      // - 在SVEState::Full状态下, 位于寄存器数据末尾(需根据向量长度对齐)
      uint32_t sve_reg_num = LLDB_INVALID_REGNUM;
       
      // 处理FPSR寄存器
      if (reg == GetRegisterInfo().GetRegNumFPSR()) {
        sve_reg_num = reg;
        if (m_sve_state == SVEState::Full)
          // 计算Full状态下FPSR的偏移量(基于向量长度)
          offset = sve::PTraceFPSROffset(sve::vq_from_vl(m_sve_header.vl));
        else if (m_sve_state == SVEState::FPSIMD)
          // FPSIMD状态下FPSR的固定偏移量(32个16字节向量后)
          offset = sve::ptrace_fpsimd_offset + (32 * 16);
           
      // 处理FPCR寄存器
      } else if (reg == GetRegisterInfo().GetRegNumFPCR()) {
        sve_reg_num = reg;
        if (m_sve_state == SVEState::Full)
          // 计算Full状态下FPCR的偏移量
          offset = sve::PTraceFPCROffset(sve::vq_from_vl(m_sve_header.vl));
        else if (m_sve_state == SVEState::FPSIMD)
          // FPSIMD状态下FPCR的固定偏移量(FPSR后4字节)
          offset = sve::ptrace_fpsimd_offset + (32 * 16) + 4;
           
      // 处理其他浮点寄存器
      } else {
        // 从寄存器信息中提取对应的SVE Z寄存器编号
        if (reg_info->value_regs && reg_info->value_regs[0] != LLDB_INVALID_REGNUM)
          sve_reg_num = reg_info->value_regs[0];
        // 计算SVE寄存器在缓冲区中的偏移量
        offset = CalculateSVEOffset(GetRegisterInfoAtIndex(sve_reg_num));
      }
 
      assert(offset < GetSVEBufferSize());
      src = (uint8_t *)GetSVEBuffer() + offset;
    }
 
  // 处理TLS(Thread Local Storage, 线程本地存储)相关寄存器
  } else if (IsTLS(reg)) {
    error = ReadTLSTPIDR();  // 读取TLS相关的TPIDR寄存器
    if (error.Fail())
      return error;
 
    // 计算TLS寄存器在缓冲区中的偏移量(相对于TLS基地址)
    offset = reg_info->byte_offset - GetRegisterInfo().GetTLSOffset();
    assert(offset < GetTLSTPIDRSize());
    src = (uint8_t *)GetTLSTPIDR() + offset;
 
  // 处理SVE(Scalable Vector Extension, 可扩展向量扩展)寄存器
  } else if (IsSVE(reg)) {
    // 检查SVE是否禁用或不支持
    if (m_sve_state == SVEState::Disabled || m_sve_state == SVEState::Unknown)
      return Status("SVE disabled or not supported");
 
    // 处理SVE向量长度寄存器(VG)
    if (GetRegisterInfo().IsSVERegVG(reg)) {
      sve_vg = GetSVERegVG();  // 获取向量长度值
      src = (uint8_t *)&sve_vg;  // 源地址指向该值
    } else {
      // 读取并缓存SVE相关的ptrace数据
      error = ReadAllSVE();
      if (error.Fail())
        return error;
 
      // 在FPSIMD状态下, SVE寄存器数据与传统fpsimd结构体兼容
      if (m_sve_state == SVEState::FPSIMD) {
        // 初始化临时缓冲区, 用0填充(非活跃部分置0)
        sve_reg_non_live.resize(reg_info->byte_size, 0);
        src = sve_reg_non_live.data();
 
        // 如果是SVE Z寄存器, 复制低16字节(与传统V寄存器兼容部分)
        if (GetRegisterInfo().IsSVEZReg(reg)) {
          offset = CalculateSVEOffset(reg_info);
          assert(offset < GetSVEBufferSize());
          ::memcpy(sve_reg_non_live.data(),
                   (uint8_t *)GetSVEBuffer() + offset, 16);
        }
      } else {
        // Full状态下直接从SVE缓冲区读取
        offset = CalculateSVEOffset(reg_info);
        assert(offset < GetSVEBufferSize());
        src = (uint8_t *)GetSVEBuffer() + offset;
      }
    }
 
  // 处理PAuth(Pointer Authentication, 指针认证)相关寄存器
  } else if (IsPAuth(reg)) {
    error = ReadPAuthMask();  // 读取PAuth掩码寄存器
    if (error.Fail())
      return error;
 
    // 计算PAuth寄存器在缓冲区中的偏移量
    offset = reg_info->byte_offset - GetRegisterInfo().GetPAuthOffset();
    assert(offset < GetPACMaskSize());
    src = (uint8_t *)GetPACMask() + offset;
 
  // 处理MTE(Memory Tagging Extension, 内存标签扩展)相关寄存器
  } else if (IsMTE(reg)) {
    error = ReadMTEControl();  // 读取MTE控制寄存器
    if (error.Fail())
      return error;
 
    // 计算MTE寄存器在缓冲区中的偏移量
    offset = reg_info->byte_offset - GetRegisterInfo().GetMTEOffset();
    assert(offset < GetMTEControlSize());
    src = (uint8_t *)GetMTEControl() + offset;
 
  // 未识别的寄存器类型
  } else
    return Status("failed - register wasn't recognized to be a GPR or an FPR, "
                  "write strategy unknown");
 
  // 将内存中的原始数据转换为RegisterValue格式(考虑字节序等)
  reg_value.SetFromMemoryData(*reg_info, src, reg_info->byte_size,
                              eByteOrderLittle, error);
 
  return error;  // 返回最终操作状态
}
// 读取ARM64架构下指定寄存器的值
// 参数:
// - reg_info: 寄存器信息结构体, 包含寄存器的各种属性(如编号、偏移量等)
// - reg_value: 输出参数, 用于存储读取到的寄存器值
// 返回值:操作状态(成功或失败信息)
Status
NativeRegisterContextLinux_arm64::ReadRegister(const RegisterInfo *reg_info,
                                               RegisterValue &reg_value) {
  Status error;  // 用于存储操作状态的变量
 
  // 检查寄存器信息指针是否为空
  if (!reg_info) {
    error.SetErrorString("reg_info NULL");  // 设置错误信息
    return error;
  }
 
  // 获取LLDB内部使用的寄存器编号(不同于硬件或DWARF编号)
  const uint32_t reg = reg_info->kinds[lldb::eRegisterKindLLDB];
 

[培训]传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2025-11-5 12:32 被nothing233编辑 ,原因:
收藏
免费 41
支持
分享
最新回复 (10)
雪    币: 5444
活跃值: (8925)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
牛啊
2025-10-12 10:04
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
太牛啦
2025-10-12 11:41
0
雪    币: 98
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
4
mark
2025-10-13 02:55
0
雪    币: 1487
活跃值: (3478)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
5
1
2025-10-13 14:23
0
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
666
2025-10-13 17:19
0
雪    币: 104
活跃值: (6838)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
tql
2025-10-13 17:20
0
雪    币: 3549
活跃值: (3700)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
6
2025-10-17 10:57
0
雪    币: 344
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
666
2025-10-17 15:59
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
10
谢谢分享
2025-10-23 10:47
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
666
2025-10-28 11:44
0
游客
登录 | 注册 方可回帖
返回