Page 20 - 《软件学报》2024年第6期
P. 20

2596                                                       软件学报  2024  年第  35  卷第  6  期


                 以检查这些在运行过程中发生改变的引用关系. 在 Go 程序中, 如果一个 store 操作可能存入指针类型, 则                         Go  编译
                 器会在编译期间在该       store 操作周围生成特定的控制流, 插入 runtime.gcWriteBarrier 函数, 该函数在       GC  时会接管
                 相应的   store 操作, 并负责帮助  GC 维护正确的内存引用关系.
                    Go 编译器只会对用户代码中包含指针的 store 操作插入 runtime.gcWriteBarrier, 且不会对一些可能带来误报
                 的运行时函数中的 store 操作插入该函数. 若能够在二进制中识别相应的结构, 就能恢复 Go 的 store 语义, 大大减
                 轻由于 Go 运行时、非指针 store 等带来的误报问题, 同时还能够减少很多对不必要的 store 插桩, 减轻动态二进制
                 插桩带来的额外开销.

                 3.2.3    利用 Go 的写屏障机制恢复    store 指针的语义
                    编译生成的与      runtime.gcWriteBarrier 相关的汇编级控制流模式如图      7  所示. 由于编译器在编译时在       store 周
                 围插入的特定控制流有固定的结构, 所以其最后生成的二进制中围绕                       runtime.gcWriteBarrier 也有特定的结构. 将
                 这些特征总结抽象, 可以形成图         7  中的特定控制流模式, 以便后续精准识别.


                 5.    bb 3 =BB(j.target)  j 的目的地址处的基本块
                                            源代码







                                      编译器转换后代码模式
                                                                         二进制控制流模式
                                           图 7 编译生成的      gcWriteBarrier 相关控制流

                    我们提出算法      1  来识别  Go  二进制中该特定的控制流模式. 其主要思路在于通过寻找特定的                    cmpl 指令来确
                 定满足要求的基本块        bb 1 , 再通过  bb 1 中的控制流跳转语句   (JNE) 来确定满足要求的两个后继基本块            bb 2 和  bb 3 ,
                 要求  bb 2 和  bb 3 有且仅有一个相同的后继.

                 算法  1. 识别图  7  中的控制流并为其中满足       store 指针语义的指令设置回调函数.
                 输入: Go 二进制文件    Bin;
                 输入: Bin  中全局变量   runtime.writeBarrier 的地址  wb.
                 运行结果: 识别出     Bin  中满足  Go  的  store 语义的指令, 并为其在  Pin  中注册运行时的回调函数
                 1. FOR ALL instr in Bin DO
                 2.  IF instr.opcode==cmpl THEN
                 3.   IF instr.operands[0]==0 且 instr.operands[1] 的有效地址 == wb THEN
                 4.    j=instr 之后最近的   jne 指令, 且该 jne 之前没有其他跳转指令

                 6.    bb 2 = BB(j.next)  j 的下一条指令处的基本块
                 7.    IF len(successors(bb 2 ))==1 且 successors(bb 2 )==successors(bb 3 ) THEN
                 8.     FOR ALL s:store in bb 2  DO
                 9.      在    store 指令  s 前注册运行时回调函数
                 10.     END FOR
                 11.     FOR ALL c:call in bb 3  DO
                 12.      IF c.targetFunc 以  runtime.gcWriteBarrier 为前缀 THEN
   15   16   17   18   19   20   21   22   23   24   25