From 39d5845dc6b817d16434bbb32bc16c0da62e123f Mon Sep 17 00:00:00 2001 From: Yuri Gribov Date: Mon, 14 Mar 2022 08:10:37 +0300 Subject: [PATCH] Support ARM assembly. --- examples/arm.asm | 51 ++++++++++++ examples/arm.pdf | Bin 0 -> 13652 bytes setup.cfg | 2 +- src/asm2cfg/asm2cfg.py | 159 +++++++++++++++++++++++++++++------- src/asm2cfg/command_line.py | 4 +- src/gdb_asm2cfg.py | 14 +++- test/test_parser.py | 75 +++++++++++++---- test/test_regex.py | 26 +++--- 8 files changed, 273 insertions(+), 58 deletions(-) create mode 100644 examples/arm.asm create mode 100644 examples/arm.pdf diff --git a/examples/arm.asm b/examples/arm.asm new file mode 100644 index 0000000..a3e0416 --- /dev/null +++ b/examples/arm.asm @@ -0,0 +1,51 @@ +00000000 : + 0: e92d40f0 push {r4, r5, r6, r7, lr} + 4: e1a05001 mov r5, r1 + 8: e24dd06c sub sp, sp, #108 ; 0x6c + c: e3a01001 mov r1, #1 + 10: e1a04000 mov r4, r0 + 14: ebfffffe bl 0 <__fcntl64_nocancel> + 18: e3700001 cmn r0, #1 + 1c: 0a000001 beq 28 + 20: e28dd06c add sp, sp, #108 ; 0x6c + 24: e8bd80f0 pop {r4, r5, r6, r7, pc} + 28: ebfffffe bl 0 <__aeabi_read_tp> + 2c: e59f307c ldr r3, [pc, #124] ; b0 + 30: e79f3003 ldr r3, [pc, r3] + 34: e7903003 ldr r3, [r0, r3] + 38: e3530009 cmp r3, #9 + 3c: 1afffff7 bne 20 + 40: e3550902 cmp r5, #32768 ; 0x8000 + 44: 059f0068 ldreq r0, [pc, #104] ; b4 + 48: 159f0068 ldrne r0, [pc, #104] ; b8 + 4c: e1a01005 mov r1, r5 + 50: e3a02000 mov r2, #0 + 54: 03a07000 moveq r7, #0 + 58: 13a07000 movne r7, #0 + 5c: 059f6058 ldreq r6, [pc, #88] ; bc + 60: 159f6058 ldrne r6, [pc, #88] ; c0 + 64: ebfffffe bl 0 <__open_nocancel> + 68: e1540000 cmp r4, r0 + 6c: 1a00000e bne ac + 70: e1a01004 mov r1, r4 + 74: e1a0200d mov r2, sp + 78: e3a00003 mov r0, #3 + 7c: ebfffffe bl 0 <__fxstat64> + 80: e3500000 cmp r0, #0 + 84: 1a000008 bne ac + 88: e59d3010 ldr r3, [sp, #16] + 8c: e2033a0f and r3, r3, #61440 ; 0xf000 + 90: e3530a02 cmp r3, #8192 ; 0x2000 + 94: 1a000004 bne ac + 98: e28d3020 add r3, sp, #32 + 9c: e893000c ldm r3, {r2, r3} + a0: e1530007 cmp r3, r7 + a4: 01520006 cmpeq r2, r6 + a8: 0affffdc beq 20 + ac: e7f000f0 udf #0 + b0: 00000078 .word 0x00000078 + b4: 0000000c .word 0x0000000c + b8: 00000000 .word 0x00000000 + bc: 00000103 .word 0x00000103 + c0: 00000107 .word 0x00000107 + diff --git a/examples/arm.pdf b/examples/arm.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5185da736daf7772765ee9fe3947c284c0f90b60 GIT binary patch literal 13652 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 KEh;C5@_zulyp?PK literal 0 HcmV?d00001 diff --git a/setup.cfg b/setup.cfg index 1797035..4ee5943 100644 --- a/setup.cfg +++ b/setup.cfg @@ -42,4 +42,4 @@ expected-line-ending-format = LF include-naming-hint = yes ignored-modules = gdb # Ignore because of the GDB integration notes= # disable warnings for TODO, FIXME etc. -disable=missing-function-docstring +disable=missing-function-docstring,no-self-use,too-many-instance-attributes,too-many-arguments,too-many-locals,too-many-branches,too-many-statements diff --git a/src/asm2cfg/asm2cfg.py b/src/asm2cfg/asm2cfg.py index 0d3c2f4..803ba47 100644 --- a/src/asm2cfg/asm2cfg.py +++ b/src/asm2cfg/asm2cfg.py @@ -23,6 +23,7 @@ def escape(instruction): instruction = instruction.replace('|', r'\|') instruction = instruction.replace('{', r'\{') instruction = instruction.replace('}', r'\}') + instruction = instruction.replace(' ', ' ') return instruction @@ -135,7 +136,7 @@ def parse_function_header(line): if function_name is not None: return InputFormat.GDB, function_name[1] - memory_range_pattern = re.compile(fr'from ({HEX_LONG_PATTERN}) to ({HEX_LONG_PATTERN}):$') + memory_range_pattern = re.compile(fr'(?:Address range|from) ({HEX_LONG_PATTERN}) to ({HEX_LONG_PATTERN}):$') memory_range = memory_range_pattern.search(line) if memory_range is not None: return InputFormat.GDB, f'{memory_range[1]}-{memory_range[2]}' @@ -191,12 +192,81 @@ def __str__(self): return ' '.join(map(lambda b: f'{b:#x}', self.bites)) +class X86TargetInfo: + """ + Contains instruction info for X86-compatible targets. + """ + + def __init__(self): + pass + + def comment(self): + return '#' + + def is_call(self, instruction): + # Various flavors of call: + # call *0x26a16(%rip) + # call 0x555555555542 + # addr32 call 0x55555558add0 + return 'call' in instruction.opcode + + def is_jump(self, instruction): + return instruction.opcode[0] == 'j' + + def is_unconditional_jump(self, instruction): + return instruction.opcode.startswith('jmp') + + def is_sink(self, instruction): + """ + Is this an instruction which terminates function execution e.g. return? + """ + return instruction.opcode.startswith('ret') + + +class ARMTargetInfo: + """ + Contains instruction info for ARM-compatible targets. + """ + + def __init__(self): + pass + + def comment(self): + return ';' + + def is_call(self, instruction): + # Various flavors of call: + # bl 0x19d90 <_IO_vtable_check> + # Note that we should be careful to not mix it with conditional + # branches like 'ble'. + return instruction.opcode.startswith('bl') \ + and instruction.opcode not in ('blt', 'ble', 'bls') + + def is_jump(self, instruction): + return instruction.opcode[0] == 'b' and not self.is_call(instruction) + + def is_unconditional_jump(self, instruction): + return instruction.opcode == 'b' + + def is_sink(self, instruction): + """ + Is this an instruction which terminates function execution e.g. return? + Detect various flavors of return like + bx lr + pop {r2-r6,pc} + Note that we do not consider conditional branches (e.g. 'bxle') to sink. + """ + return re.search(r'\bpop\b.*\bpc\b', instruction.body) \ + or (instruction.opcode == 'bx' and instruction.ops[0] == 'lr') \ + or instruction.opcode == 'udf' + + class Instruction: """ Represents a single assembly instruction with it operands, location and optional branch target """ - def __init__(self, body, text, lineno, address, opcode, ops, target, imm): # pylint: disable=too-many-arguments + def __init__(self, body, text, lineno, address, opcode, ops, target, imm, target_info): # noqa self.body = body self.text = text self.lineno = lineno @@ -204,6 +274,7 @@ def __init__(self, body, text, lineno, address, opcode, ops, target, imm): # py self.opcode = opcode self.ops = ops self.target = target + self.info = target_info if imm is not None and (self.is_jump() or self.is_call()): if self.target is None: self.target = imm @@ -211,22 +282,19 @@ def __init__(self, body, text, lineno, address, opcode, ops, target, imm): # py self.target.merge(imm) def is_call(self): - # Various flavors of call: - # call *0x26a16(%rip) - # call 0x555555555542 - # addr32 call 0x55555558add0 - # TODO: here and elsewhere support other target platforms - return 'call' in self.opcode + return self.info.is_call(self) def is_jump(self): - return self.opcode[0] == 'j' + return self.info.is_jump(self) + + def is_direct_jump(self): + return self.is_jump() and re.match(fr'{HEX_LONG_PATTERN}', self.ops[0]) def is_sink(self): - return self.opcode.startswith('ret') + return self.info.is_sink(self) - # TODO: handle sink instructions like retq def is_unconditional_jump(self): - return self.opcode.startswith('jmp') + return self.info.is_unconditional_jump(self) def __str__(self): result = f'{self.address}: {self.opcode}' @@ -246,27 +314,41 @@ def parse_address(line): return address, address_match[3] +def split_nth(string, count): + """ + Splits string to equally-sized chunks + """ + return [string[i:i+count] for i in range(0, len(string), count)] + + def parse_encoding(line): """ Parses byte encoding of instruction for objdump disassemblies e.g. the '31 c0' in '16bd3: 31 c0 xor %eax,%eax' + In addition to X86 supports ARM encoding styles: + '4: e1a01000 mov r1, r0' + '50: f7ff fffe bl 0 <__aeabi_dadd>' + '54: 0002 movs r2, r0' """ # Encoding is separated from assembly mnemonic via tab # so we allow whitespace separators between bytes # to avoid accidentally matching the mnemonic. - enc_match = re.match(r'^\s*((?:[0-9a-f][0-9a-f] +)+)(.*)', line) + enc_match = re.match(r'^\s*((?:[0-9a-f]{2,8} +)+)(.*)', line) if enc_match is None: return None, line - bites = [int(byte, 16) for byte in enc_match[1].strip().split(' ')] + bites = [] + for chunk in enc_match[1].strip().split(' '): + bites.extend(int(byte, 16) for byte in split_nth(chunk, 2)) return Encoding(bites), enc_match[2] -def parse_body(line): +def parse_body(line, target_info): """ Parses instruction body (opcode and operands) """ - body_match = re.match(r'^\s*([^#<]+)(.*)', line) + comment_symbol = target_info.comment() + body_match = re.match(fr'^\s*([^{comment_symbol}<]+)(.*)', line) if body_match is None: return None, None, None, line body = body_match[1].strip() @@ -291,13 +373,21 @@ def parse_target(line): return address, target_match[3] -def parse_imm(line): +def parse_comment(line, target_info): """ - Parses optional instruction imm hint + Parses optional instruction comment """ - imm_match = re.match(fr'^\s*#\s*(?:0x)?({HEX_PATTERN})\s*(<.*>)?(.*)', line) - if imm_match is None: + comment_symbol = target_info.comment() + comment_match = re.match(fr'^\s*{comment_symbol}\s*(.*)', line) + if comment_match is None: return None, line + comment = comment_match[1] + imm_match = re.match(fr'^(?:0x)?({HEX_PATTERN})\s*(<.*>)?(.*)', comment) + if imm_match is None: + # If no imm was found, ignore the comment. + # In particular this takes care of useless ARM comments like + # '82: 46c0 nop ; (mov r8, r8)' + return None, '' abs_addr = int(imm_match[1], 16) if imm_match[2]: target, _ = parse_target(imm_match[2]) @@ -307,7 +397,7 @@ def parse_imm(line): return target, imm_match[3] -def parse_line(line, lineno, function_name, fmt): +def parse_line(line, lineno, function_name, fmt, target_info): """ Parses a single line of assembly to create Instruction instance """ @@ -328,13 +418,13 @@ def parse_line(line, lineno, function_name, fmt): return encoding original_line = line - body, opcode, ops, line = parse_body(line) + body, opcode, ops, line = parse_body(line, target_info) if opcode is None: return None target, line = parse_target(line) - imm, line = parse_imm(line) + imm, line = parse_comment(line, target_info) if line: # Expecting complete parse return None @@ -345,7 +435,7 @@ def parse_line(line, lineno, function_name, fmt): if target is not None and target.base is None: target.base = function_name - return Instruction(body, original_line, lineno, address, opcode, ops, target, imm) + return Instruction(body, original_line, lineno, address, opcode, ops, target, imm, target_info) class JumpTable: @@ -365,7 +455,7 @@ def __init__(self, instructions): # Iterate over the lines and collect jump targets and branching points. for inst in instructions: - if inst is None or not inst.is_jump(): + if inst is None or not inst.is_direct_jump(): continue self.abs_sources[inst.address.abs] = inst.target @@ -389,7 +479,15 @@ def get_target(self, address): return None -def parse_lines(lines, skip_calls): # noqa pylint: disable=too-many-locals,too-many-branches,too-many-statements,unused-argument +def parse_lines(lines, skip_calls, target_name): # noqa pylint: disable=unused-argument + if target_name == 'x86': + target_info = X86TargetInfo() + elif target_name == 'arm': + target_info = ARMTargetInfo() + else: + print(f'Unsupported platform {target_name}') + sys.exit(1) + instructions = [] current_function_name = current_format = None for num, line in enumerate(lines, 1): @@ -402,7 +500,7 @@ def parse_lines(lines, skip_calls): # noqa pylint: disable=too-many-locals,too- current_format = fmt continue - instruction_or_encoding = parse_line(line, num, current_function_name, current_format) + instruction_or_encoding = parse_line(line, num, current_function_name, current_format, target_info) if isinstance(instruction_or_encoding, Encoding): # Partial encoding for previous instruction, skip it continue @@ -413,13 +511,16 @@ def parse_lines(lines, skip_calls): # noqa pylint: disable=too-many-locals,too- if line.startswith('End of assembler dump') or not line: continue + if line.strip() == '': + continue + print(f'Unexpected assembly at line {num}:\n {line}') sys.exit(1) # Infer target address for jump instructions for instruction in instructions: if (instruction.target is None or instruction.target.abs is None) \ - and instruction.is_jump(): + and instruction.is_direct_jump(): if instruction.target is None: instruction.target = Address(0) instruction.target.abs = int(instruction.ops[0], 16) @@ -497,7 +598,7 @@ def parse_lines(lines, skip_calls): # noqa pylint: disable=too-many-locals,too- # If last instruction of the function is jump/call, then add dummy # block to designate end of the function. end_block = BasicBlock('end_of_function') - dummy_instruction = Instruction('', 'end of function', 0, None, None, [], None, None) + dummy_instruction = Instruction('', 'end of function', 0, None, None, [], None, None, target_info) end_block.add_instruction(dummy_instruction) previous_jump_block.add_no_jump_edge(end_block.key) basic_blocks[end_block.key] = end_block diff --git a/src/asm2cfg/command_line.py b/src/asm2cfg/command_line.py index 3d08ead..1a5c8eb 100644 --- a/src/asm2cfg/command_line.py +++ b/src/asm2cfg/command_line.py @@ -16,10 +16,12 @@ def main(): help='File to contain one function assembly dump') parser.add_argument('-c', '--skip-calls', action='store_true', help='Skip function calls from dividing code to blocks') + parser.add_argument('--target', choices=['x86', 'arm'], default='x86', + help='Specify target platform for assembly') parser.add_argument('-v', '--view', action='store_true', help='View as a dot graph instead of saving to a file') args = parser.parse_args() print('If function CFG rendering takes too long, try to skip function calls with -c flag') lines = asm2cfg.read_lines(args.assembly_file) - function_name, basic_blocks = asm2cfg.parse_lines(lines, args.skip_calls) + function_name, basic_blocks = asm2cfg.parse_lines(lines, args.skip_calls, args.target) asm2cfg.draw_cfg(function_name, basic_blocks, args.view) diff --git a/src/gdb_asm2cfg.py b/src/gdb_asm2cfg.py index ff41e6b..5d68ad7 100644 --- a/src/gdb_asm2cfg.py +++ b/src/gdb_asm2cfg.py @@ -55,8 +55,17 @@ def __init__(self): def invoke(self, _arg, _from_tty): # pylint: disable=no-self-use """ Called by GDB when viewcfg command is invoked """ try: + frame = gdb.selected_frame() + arch = frame.architecture().name() + if arch.startswith('i386'): + target_name = 'x86' + elif arch.startswith('arm'): + target_name = 'arm' + else: + raise RuntimeError(f'unknown platform: {arch}') assembly_lines = gdb.execute('disassemble', from_tty=False, to_string=True).split('\n') - function_name, basic_blocks = asm2cfg.parse_lines(assembly_lines, gdb.parameter('skipcalls')) + function_name, basic_blocks = asm2cfg.parse_lines(assembly_lines, gdb.parameter('skipcalls'), + target_name) asm2cfg.draw_cfg(function_name, basic_blocks, view=True) # Catch error coming from GDB side before other errors. except gdb.error as ex: @@ -80,7 +89,8 @@ def invoke(self, _arg, _from_tty): # pylint: disable=no-self-use """ Called by GDB when savecfg command is invoked """ try: assembly_lines = gdb.execute('disassemble', from_tty=False, to_string=True).split('\n') - function_name, basic_blocks = asm2cfg.parse_lines(assembly_lines, gdb.parameter('skipcalls')) + function_name, basic_blocks = asm2cfg.parse_lines(assembly_lines, gdb.parameter('skipcalls'), + 'x86') asm2cfg.draw_cfg(function_name, basic_blocks, view=False) # Catch error coming from GDB side before other errors. except gdb.error as ex: diff --git a/test/test_parser.py b/test/test_parser.py index c48cbb0..75f6cfe 100644 --- a/test/test_parser.py +++ b/test/test_parser.py @@ -17,9 +17,13 @@ class ParseLineTestCase(unittest.TestCase): Tests of parse_line function """ + def setUp(self): + self.target_info = asm2cfg.X86TargetInfo() + self.arm_target_info = asm2cfg.ARMTargetInfo() + def test_simple_inst(self): line = '0x000055555556f957 <+7>: push %r14' - i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB) + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB, self.target_info) self.assertIsNot(i, None) self.assertEqual(i.body, 'push %r14') @@ -38,7 +42,7 @@ def test_jump(self): line = '''\ 0x00007ffff7fbf26b <+395>: jmp 0x7ffff7fbf55d \ ''' - i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB) + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB, self.target_info) self.assertIsNot(i, None) self.assertEqual(i.body, 'jmp 0x7ffff7fbf55d') @@ -60,7 +64,7 @@ def test_branch(self): line = '''\ 0x00007ffff7fbf565 <+1157>: je 0x7ffff7fbf635 \ ''' - i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB) + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB, self.target_info) self.assertIsNot(i, None) self.assertEqual(i.body, 'je 0x7ffff7fbf635') @@ -82,7 +86,7 @@ def test_call(self): line = '''\ 0x000000000002ec0f <+63>: callq 0x2eab0 <__sigsetjmp@plt> ''' - i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB) + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB, self.target_info) self.assertIsNot(i, None) self.assertEqual(i.body, 'callq 0x2eab0') @@ -104,7 +108,7 @@ def test_call_stripped(self): line = '''\ 0x000055555556f9b0 <+96>: call *0x2731a(%rip) # 0x555555596cd0 ''' - i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB) + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.GDB, self.target_info) self.assertIsNot(i, None) self.assertEqual(i.body, 'call *0x2731a(%rip)') @@ -126,10 +130,7 @@ def test_objdump(self): line = '''\ 16bbb: 74 29 je 16be6 <_obstack_allocated_p@@Base+0x36> ''' - i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.OBJDUMP) - - self.assertTrue(i.is_jump()) - self.assertFalse(i.is_unconditional_jump()) + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.OBJDUMP, self.target_info) self.assertIsNot(i, None) self.assertEqual(i.body, 'je 16be6') @@ -142,6 +143,50 @@ def test_objdump(self): self.assertEqual(i.target.base, '_obstack_allocated_p@@Base') self.assertIs(i.target.offset, 0x36) + self.assertTrue(i.is_jump()) + self.assertFalse(i.is_unconditional_jump()) + + def test_arm_branch(self): + line = '''\ + 1c: 0a000001 beq 28 +''' + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.OBJDUMP, self.arm_target_info) + + self.assertIsNot(i, None) + self.assertEqual(i.body, 'beq 28') + self.assertEqual(i.lineno, 1) + self.assertIsNot(i.address, None) + self.assertEqual(i.address.abs, 0x1c) + self.assertIs(i.address.base, 'main') + self.assertIsNot(i.target, None) + self.assertIs(i.target.abs, None) + self.assertEqual(i.target.base, 'check_one_fd') + self.assertIs(i.target.offset, 0x28) + + self.assertTrue(i.is_jump()) + self.assertFalse(i.is_unconditional_jump()) + + + def test_arm_jump(self): + line = '''\ + 1c: 0a000001 b 28 +''' + i = asm2cfg.parse_line(line, 1, 'main', asm2cfg.InputFormat.OBJDUMP, self.arm_target_info) + + self.assertIsNot(i, None) + self.assertEqual(i.body, 'b 28') + self.assertEqual(i.lineno, 1) + self.assertIsNot(i.address, None) + self.assertEqual(i.address.abs, 0x1c) + self.assertIs(i.address.base, 'main') + self.assertIsNot(i.target, None) + self.assertIs(i.target.abs, None) + self.assertEqual(i.target.base, 'check_one_fd') + self.assertIs(i.target.offset, 0x28) + + self.assertTrue(i.is_jump()) + self.assertTrue(i.is_unconditional_jump()) + class ParseLinesTestCase(unittest.TestCase): """ @@ -158,7 +203,7 @@ def test_linear_sequence(self): 0x000055555556f95d <+13>: push %rbp 0x000055555556f95e <+14>: push %rbx\ '''.split('\n') - _, blocks = asm2cfg.parse_lines(lines, False) + _, blocks = asm2cfg.parse_lines(lines, False, 'x86') self.assertEqual(len(blocks), 1) _, block = blocks.popitem() @@ -173,7 +218,7 @@ def test_unconditional(self): 0x0000555555570058 <+1800>: mov 0xe0(%rsp),%rdi 0x0000555555570060 <+1808>: test %rdi,%rdi '''.split('\n') - _, blocks = asm2cfg.parse_lines(lines, False) + _, blocks = asm2cfg.parse_lines(lines, False, 'x86') self.assertEqual(len(blocks), 2) @@ -197,7 +242,7 @@ def test_conditional(self): 0x0000555555570058 <+1800>: mov 0xe0(%rsp),%rdi 0x0000555555570060 <+1808>: test %rdi,%rdi '''.split('\n') - _, blocks = asm2cfg.parse_lines(lines, False) + _, blocks = asm2cfg.parse_lines(lines, False, 'x86') self.assertEqual(len(blocks), 3) @@ -223,7 +268,7 @@ def test_return(self): 0x0000555555570058 <+1800>: mov 0xe0(%rsp),%rdi 0x0000555555570060 <+1808>: test %rdi,%rdi '''.split('\n') - _, blocks = asm2cfg.parse_lines(lines, False) + _, blocks = asm2cfg.parse_lines(lines, False, 'x86') self.assertEqual(len(blocks), 2) @@ -286,7 +331,7 @@ def test_jumptables(self): 0x000000000000111a <+170>: nopw 0x0(%rax,%rax,1) 0x0000000000001120 <+176>: retq '''.split('\n') - _, blocks = asm2cfg.parse_lines(lines, False) + _, blocks = asm2cfg.parse_lines(lines, False, 'x86') # TODO: special block for indirect jumps self.assertEqual(len(blocks), 4) @@ -297,7 +342,7 @@ def test_dummy_block(self): 0x000055555556fffb <+1709>: push %rbx 0x000055555556fffd <+1707>: je 0x000055555556fffb '''.split('\n') - _, blocks = asm2cfg.parse_lines(lines, False) + _, blocks = asm2cfg.parse_lines(lines, False, 'x86') self.assertEqual(len(blocks), 2) diff --git a/test/test_regex.py b/test/test_regex.py index d1568ee..79077ab 100644 --- a/test/test_regex.py +++ b/test/test_regex.py @@ -65,9 +65,12 @@ class ParseBodyTestCase(unittest.TestCase): Tests of asm2cfg.parse_body function """ + def setUp(self): + self.target_info = asm2cfg.X86TargetInfo() + def test_gdb_stripped_known(self): line = ' call 0x55555558add0 <_Z19exportDebugifyStats>' - body, opcode, ops, rest = asm2cfg.parse_body(line) + body, opcode, ops, rest = asm2cfg.parse_body(line, self.target_info) self.assertIsNot(body, None) self.assertEqual(body, 'call 0x55555558add0') @@ -77,7 +80,7 @@ def test_gdb_stripped_known(self): def test_gdb_stripped_pic(self): line = ' call *0x26a16(%rip) # 0x5555555967a8' - body, opcode, ops, rest = asm2cfg.parse_body(line) + body, opcode, ops, rest = asm2cfg.parse_body(line, self.target_info) self.assertIsNot(body, None) self.assertEqual(body, 'call *0x26a16(%rip)') @@ -87,7 +90,7 @@ def test_gdb_stripped_pic(self): def test_gdb_plt(self): line = ' callq 0x1020 ' - body, opcode, ops, rest = asm2cfg.parse_body(line) + body, opcode, ops, rest = asm2cfg.parse_body(line, self.target_info) self.assertIsNot(body, None) self.assertEqual(body, 'callq 0x1020') @@ -97,7 +100,7 @@ def test_gdb_plt(self): def test_gdb_stripped_nonpic(self): line = ' call 0x555555555542' - body, opcode, ops, rest = asm2cfg.parse_body(line) + body, opcode, ops, rest = asm2cfg.parse_body(line, self.target_info) self.assertIsNot(body, None) self.assertEqual(body, 'call 0x555555555542') @@ -107,7 +110,7 @@ def test_gdb_stripped_nonpic(self): def test_gdb_indirect_call(self): line = ' callq *(%rsi)' - body, opcode, ops, rest = asm2cfg.parse_body(line) + body, opcode, ops, rest = asm2cfg.parse_body(line, self.target_info) self.assertIsNot(body, None) self.assertEqual(body, 'callq *(%rsi)') @@ -152,14 +155,17 @@ def test_without_offset(self): self.assertEqual(rest, '') -class ParseImmTestCase(unittest.TestCase): +class ParseCommentTestCase(unittest.TestCase): """ - Tests of parse_imm function + Tests of parse_comment function """ + def setUp(self): + self.target_info = asm2cfg.X86TargetInfo() + def test_absolute(self): line = '# 0x5555555967a8' - address, rest = asm2cfg.parse_imm(line) + address, rest = asm2cfg.parse_comment(line, self.target_info) self.assertIsNot(address, None) self.assertEqual(address.abs, 0x5555555967a8) @@ -169,7 +175,7 @@ def test_absolute(self): def test_symbolic(self): line = '# 0x5555555967a8 ' - address, rest = asm2cfg.parse_imm(line) + address, rest = asm2cfg.parse_comment(line, self.target_info) self.assertIsNot(address, None) self.assertEqual(address.abs, 0x5555555967a8) @@ -179,7 +185,7 @@ def test_symbolic(self): def test_complete(self): line = '# 3ff8 ' - address, rest = asm2cfg.parse_imm(line) + address, rest = asm2cfg.parse_comment(line, self.target_info) self.assertIsNot(address, None) self.assertEqual(address.abs, 0x3ff8) # FIXME: support hex offsets