我正在编写一个框架来实现在模拟器和未修改的主机软件中运行的RTL的协同仿真。编写主机软件以控制实际硬件,通常以两种方式之一工作:
前一种情况非常简单 - 编写一个库,它实现与驱动程序相同的读/写调用,并在运行模拟时与之相关联。这一切都非常有效,我可以运行未经修改的生产软件作为我的RTL模拟的刺激。
第二种情况比第一种情况要困难得多......
最初我以为我可以使用LD_PRELOAD
拦截mmap调用。在我mmap
的实现中,我会分配一些页面对齐的内存然后mprotect
并设置一个信号处理程序来捕获SIGSEGV
。
这种方法存在许多问题:
读取与写入
我可以确定来自siginfo_t->si_addr
的访问地址,但不能确定访问是读还是写。
捕获重复访问
在信号处理程序中,我需要取消保护内存区域,否则只要我的处理程序退出并且主机代码永远不会继续,我就会重复SIGSEGV
。但是,如果我取消保护区域,那么我的信号处理程序将不会捕获后续访问。
信号处理程序肮脏
在模拟器驱动RTL并返回结果时阻塞信号处理程序违反了各种编程规则 - 特别是假设模拟器可以触发各种其他事件并在从此访问返回结果之前执行任意代码。 / p>
我想知道是否可以创建一个类似文件的对象,其行为类似于磁盘,而不是在缓冲区上使用mprotect
。我没有找到任何表明这是可行的信息。
是否可以捕获对mmap区域的所有访问以及如何?
假设LD_PRELOAD
和mprotect
是最佳路线:
mprotect
区域?答案 0 :(得分:6)
在X86上,您可以为调用者的上下文设置Trap标志,以便在一条指令之后获得SIGTRAP(此标志通常用于单步执行)。也就是说,遇到SIGSEGV时,在调用者的EFLAGS中设置TF(参见ucontext.h
),启用mprotect
读取并返回。如果使用相同的IP立即重复SIGSEGV,则启用写入(如果要区分读取 - 修改 - 写入与只写访问,则可以选择禁用读取)。如果从同一IP获取SIGSEGV以进行只读和只写保护,则启用读写。
无论何时获得SIGTRAP,您都可以分析写入的值(如果是写访问权限),还可以重新保护页面以捕获将来的访问权。
更正:如果读取和写入都有副作用,请先尝试只写保护,然后应用读取副作用并尝试只读保护,然后在最终的SIGTRAP中启用写入和处理写入的副作用处理程序。
更新:我推荐假设的只写保护,这在大多数架构中都不存在,这是致命的错误。幸运的是,有一种更简单的方法可以知道失败的操作是否尝试读取内存,至少在x86上是这样的:
页面错误异常将错误代码推送到堆栈,该堆栈在Linux SIGSEGV处理程序中可用作err
结构的sigcontext
成员。对于写错误,Bit 1 of the error code为1,否则为0。对于读 - 修改 - 写操作,它最初将为0(这里你可以模拟读数,确切地知道它会发生)。