• 沒有找到結果。

III. Design and Implementation

3.3. Implementation Detail

3.3.2. Address Mapping Table

Address mapping table (AMT) is created for finding indirect branch targets. The smaller the table is, the shorter the table looking time is. There are only two kinds of possible entries can be indirect branch targets: function entry and return addresses.

25

3.3.2.1. Function Entry

Function entries can be found in the symbol table, and this is the easiest case.

Unfortunately, sometimes the symbol table is stripped, and our analyzer must have an ability to find the function entries.

There are several possible patterns can be considered as function entries; however, useless entries may also be regarded as function entries, so our analyzer must have ability to handle these entries. Besides, different cases may mark the same address as a function entry, our analyzer don’t discard them since this information can be regarded as a profiling result. Since not all entries found in all cases are real function entries, our analyzer can regard these entries as function entries if it is hit more than certain times. In our experience, the patterns shown below can be a function entry.

1) The address of PUSH instruction with LR included.

2) The address of a 32-bits STMDB instruction with base register being SP and LR is included in operands. The behavior of this STMDB instruction is the same as PUSH instruction.

3) The next instruction immediately follows a POP instruction whose operands include PC.

4) The next instruction immediately follows a 32-bits LDMIA instruction with base register being SP and PC is included in operands. The behavior of this LDMIA instruction is the same as POP instruction.

000a0f2c <__GI_getpid>:

a0f2c: b543 push {r0, r1, r6, lr}

a0f3c: bd4c pop {r2, r3, r6, pc}

a0f3e: bf00 nop 000a0f40 <__GI_gettimeofday>:

a0f40: b557 push {r0, r1, r2, r4, r6, lr}

26

a0f68: bd5e pop {r1, r2, r3, r4, r6, pc}

a0f6a: bf00 nop

Figure 18. An example of PUSH and POP in finding function entries

5) The next instruction immediately follows a BX instruction with operand being LR.

000a59e0 <__pthread_mutex_lock>:

a59e0: 2000 movs r0, #0 a59e2: 4770 bx lr

000a59e4 <__pthread_mutex_init>:

a59e4: 2000 movs r0, #0 a59e6: 4770 bx lr

000a59e8 <_pthread_cleanup_push_defer>:

a59e8: 6001 str r1, [r0, #0]

a59ea: 6042 str r2, [r0, #4]

a59ec: 4770 bx lr

Figure 19. An example of BX in finding function entries

6) The branch target of BL and BLX instruction. Entries found in this case must be function entries, so our analyzer have to add all of them in the AMT.

7) The address of Function entries may be stored in the PC-relative data. Our analyzer has to check whether the data can be regarded as an entry, and adds it to the AMT if true.

8) The instructions that follow NOP or NOP.W instruction. Since NOP instructions are used for padding and make the address align certain bytes; therefore, this

instruction has higher possibility to be put at the end of the B-function.

Sometimes there is a NOP instruction before the function entry because of the

alignment issue, that is, our analyzer may regard the NOP instruction as a function entry in 3), 4) and 5). Our analyzer has to check whether this instruction exists for fear that adding no use entry in the AMT.

All of the B-functions end with branch instructions, no matter indirect or direct. Most of

27

the cases have been included as described above, but the cases that a B-function end with an unconditional branch instruction is not included.

Conditional branches can’t be the end of B-function because the control flow must reach the next instruction if the condition fails, and instruction address following function call instructions are regarded as the return address, which describes in 3.3.2.2. As a result, only the address of instruction following B or B.W must be recorded. However, putting all of this kind of addresses in the AMT may cause the optimization time and compiler time much longer, even exhausting the memory. Therefore, our analyzer stores all of this kind of

addresses in a secondary address mapping table, and lets the output binary search it if the searching of primary AMT fails. In our system, we maintain this secondary AMT by using LLVM switch instructions, which is the same as the original AMT and described in 3.3.2.3, and LLVM indirect branch instructions, which calls a helper function to search the target before it.

3.3.2.2. Function Return Address

Return address is the address that the function returns to. It is the address of the instruction that is immediately follows the function call instruction, like BL and BLX. Our analyzer stores all of this kind of entries in the AMT.

3.3.2.3. The AMT in LLVM IR

Our translator generate the LLVM switch instruction to handle the AMT, because it knows all of the entries from our analyzer, and using switch instruction is easier to generate direct jump to different entries. Since our analyzer only gets a portion of possible entries from the input binary, the AMT table will be too sparse if we put all of the entries in an LLVM switch instruction. Therefore, the compilers will generate a sequence of if-else instructions for switch instruction instead of a jump table. This will result in a bad performance for searching an address in the AMT. To solve this problem, we use a modulo-function as a hash function to split a large switch statement into several small switch statements.

Figure 20 shows an example of the AMT. The number of tables is dependent on how many possible entries our analyzer found, and it is assumed a power of two because simpler operation can be used when hashing. As a result, even the entry addresses in the level-2 table are still sparse, the number of if-else instructions must be much smaller than the one

28

without hashing.

Figure 20. Diagram of the Address Mapping Table

相關文件