Skip to content

Commit

Permalink
Tests of jump and function tables. (#13)
Browse files Browse the repository at this point in the history
* Added code snippets for jumptables.

* Added -fno-plt mode for %rip-based calls.

* Added todo.

* Use itertools.product instead of nested loops.

* Added tests for indirect pointers.
  • Loading branch information
yugr authored Jan 16, 2022
1 parent 182516f commit e980e52
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 65 deletions.
5 changes: 5 additions & 0 deletions test/templates/funtable.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
int bar(int x, void (*f[])(int x)) {
if (x > 0)
f[0](x);
f[1](x);
}
72 changes: 39 additions & 33 deletions test/templates/gen_calls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,56 +5,62 @@
"""

# TODO: find a way to produce other snippets we see in disasm:
# call *0x273a0(%rip)
# call *%rax
# call *0x8(%rax)
# addr32 call 0x5555555733e0
# call 0x555555576a00

import os.path
import itertools

from common import set_basename, gcc, disasm, grep

set_basename(os.path.basename(__file__))

for gdb in [False, True]:
for pic in [False, True]: # Do we need to test PIE too?
for direct in [False, True] if pic else [True]:
for strip in [False, True]:
# Print config
for gdb, pic, plt, direct, strip in itertools.product([False, True],
[False, True], # Do we need to test PIE too?
[False, True],
[False, True],
[False, True]):
# Print config

disasm_type = 'GDB' if gdb else 'objdump'
pic_type = 'position-INdependent' if pic else 'position-dependent'
call_type = 'PIC-call' if direct else 'PLT-call'
strip_type = 'stripped' if strip else 'UNstripped'
print(f"Checking {disasm_type} {pic_type} {call_type} {strip_type}")
disasm_type = 'GDB' if gdb else 'objdump'
pic_type = 'position-INdependent' if pic else 'position-dependent'
call_type = 'Non-PIC-call' if direct else 'PIC-call'
strip_type = 'stripped' if strip else 'UNstripped'
plt_type = 'PLT' if plt else 'PLT-less'
print(f"Checking {disasm_type} {pic_type} {plt_type} {call_type} {strip_type}")

# Generate object code
# Generate object code

flags = ['call.c', '-o', 'a.out',
'-Wl,--defsym,_start=0', '-nostdlib', '-nostartfiles']
# DLL or executable?
if pic:
flags += ['-fPIC', '-shared']
# Force non-PLT call for PIC code?
if direct and pic:
flags.append('-DHIDDEN')
# Include debuginfo?
if not strip:
flags.append('-g')
flags = ['call.c', '-o', 'a.out',
'-Wl,--defsym,_start=0', '-nostdlib', '-nostartfiles']
# DLL or executable?
if pic:
flags += ['-fPIC', '-shared']
# Use PLT?
if not plt:
flags += ['-fno-plt']
# Force non-PLT call for PIC code?
if direct and pic:
flags.append('-DHIDDEN')
# Include debuginfo?
if not strip:
# FIXME: for real stripping of symtab we need to run
# `strip a.out` and `strip -s a.out`.
# This should be done for other tests too.
flags.append('-g')

gcc(flags)
gcc(flags)

# Generate disasm
# Generate disasm

caller = 'bar'
out = disasm('a.out', not gdb, strip, caller)
caller = 'bar'
out = disasm('a.out', not gdb, strip, caller)

# Print snippets
# Print snippets

headers = grep(out, fr'<{caller}>:|Dump of')
calls = grep(out, r'call')
print('''\
headers = grep(out, fr'<{caller}>:|Dump of')
calls = grep(out, r'call')
print('''\
headers:
{0}
calls:
Expand Down
48 changes: 48 additions & 0 deletions test/templates/gen_funtable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python3

"""
Generate different flavors of input assembly for testing.
"""

import os.path
import itertools

from common import set_basename, gcc, disasm, grep

set_basename(os.path.basename(__file__))

for gdb, pic, strip in itertools.product([False, True],
[False, True], # Do we need to test PIE too?
[False, True]):
# Print config

disasm_type = 'GDB' if gdb else 'objdump'
pic_type = 'position-INdependent' if pic else 'position-dependent'
stripped = 'stripped' if strip else 'UNstripped'
print(f"Checking {disasm_type} {pic_type} {stripped}")

# Generate object code

flags = ['funtable.c', '-o', 'a.out',
'-Wl,--defsym,_start=0', '-nostdlib', '-nostartfiles', '-O2']
# DLL or executable?
if pic:
flags += ['-fPIC', '-shared']
# Include debuginfo?
if not strip:
flags.append('-g')

gcc(flags)

# Generate disasm

caller = 'bar'
out = disasm('a.out', not gdb, strip, caller)

# Print snippets

jumps = grep(out, r'\bcall')
print('''\
table calls:
{}
'''.format('\n '.join(jumps)))
57 changes: 29 additions & 28 deletions test/templates/gen_jumps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,48 @@
"""

import os.path
import itertools

from common import set_basename, gcc, disasm, grep

set_basename(os.path.basename(__file__))

for gdb in [False, True]:
for pic in [False, True]: # Do we need to test PIE too?
for opt in [False, True]:
for strip in [False, True]:
# Print config
for gdb, pic, opt, strip in itertools.product([False, True],
[False, True], # Do we need to test PIE too?
[False, True],
[False, True]):
# Print config

disasm_type = 'GDB' if gdb else 'objdump'
pic_type = 'position-INdependent' if pic else 'position-dependent'
opt_type = 'optimized' if opt else 'UNoptimized'
stripped = 'stripped' if strip else 'UNstripped'
print(f"Checking {disasm_type} {pic_type} {opt_type} {stripped}")
disasm_type = 'GDB' if gdb else 'objdump'
pic_type = 'position-INdependent' if pic else 'position-dependent'
opt_type = 'optimized' if opt else 'UNoptimized'
stripped = 'stripped' if strip else 'UNstripped'
print(f"Checking {disasm_type} {pic_type} {opt_type} {stripped}")

# Generate object code
# Generate object code

flags = ['jump.c', '-o', 'a.out',
'-Wl,--defsym,_start=0', '-nostdlib', '-nostartfiles']
# DLL or executable?
if pic:
flags += ['-fPIC', '-shared']
if opt:
flags.append('-O2')
# Include debuginfo?
if not strip:
flags.append('-g')
flags = ['jump.c', '-o', 'a.out',
'-Wl,--defsym,_start=0', '-nostdlib', '-nostartfiles']
# DLL or executable?
if pic:
flags += ['-fPIC', '-shared']
if opt:
flags.append('-O2')
# Include debuginfo?
if not strip:
flags.append('-g')

gcc(flags)
gcc(flags)

# Generate disasm
# Generate disasm

caller = 'bar'
out = disasm('a.out', not gdb, strip, caller)
caller = 'bar'
out = disasm('a.out', not gdb, strip, caller)

# Print snippets
# Print snippets

jumps = grep(out, r'\bj')
print('''\
jumps = grep(out, r'\bj')
print('''\
jumps:
{}
'''.format('\n '.join(jumps)))
48 changes: 48 additions & 0 deletions test/templates/gen_jumptable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env python3

"""
Generate different flavors of input assembly for testing.
"""

import os.path
import itertools

from common import set_basename, gcc, disasm, grep

set_basename(os.path.basename(__file__))

for gdb, pic, strip in itertools.product([False, True],
[False, True], # Do we need to test PIE too?
[False, True]):
# Print config

disasm_type = 'GDB' if gdb else 'objdump'
pic_type = 'position-INdependent' if pic else 'position-dependent'
stripped = 'stripped' if strip else 'UNstripped'
print(f"Checking {disasm_type} {pic_type} {stripped}")

# Generate object code

flags = ['jumptable.c', '-o', 'a.out',
'-Wl,--defsym,_start=0', '-nostdlib', '-nostartfiles', '-O2']
# DLL or executable?
if pic:
flags += ['-fPIC', '-shared']
# Include debuginfo?
if not strip:
flags.append('-g')

gcc(flags)

# Generate disasm

caller = 'bar'
out = disasm('a.out', not gdb, strip, caller)

# Print snippets

jumps = grep(out, r'\bjmp')
print('''\
table jumps:
{}
'''.format('\n '.join(jumps)))
27 changes: 27 additions & 0 deletions test/templates/jumptable.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
__attribute__((noinline)) void foo() {}
__attribute__((noinline)) void baz() {}

int bar(int x) {
// Should generate jumptable
switch (x) {
case 0:
return 10;
case 1:
baz("-14");
case 3:
return -25;
case 4:
return -27;
case 5:
return 35;
case 6:
return 99;
case 7:
return 33;
case 8:
return 12;
case 9:
foo();
return 12;
}
}
Loading

0 comments on commit e980e52

Please sign in to comment.