diff --git a/CHANGELOG.md b/CHANGELOG.md index c2b9d560..98a6a0e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Better data access computation for MIPS binaries. * Detect incremental linking regions in PE binaries. * Create elfStackSize and elfStackExec auxdata from ELF PT_GNU_STACK segments. +* In PE binaries, every exported code symbol is considered a function entry. * Requires gtirb >=1.12.1, gtirb-pprinter >=2.0.0 # 1.7.0 diff --git a/examples/asm_examples/ex_dll_export_thunk/Makefile.windows b/examples/asm_examples/ex_dll_export_thunk/Makefile.windows new file mode 100644 index 00000000..ed93a4bc --- /dev/null +++ b/examples/asm_examples/ex_dll_export_thunk/Makefile.windows @@ -0,0 +1,5 @@ +all: + ml64.exe /c ex.asm + link.exe ex.obj /DLL /SUBSYSTEM:WINDOWS +clean: + del /F /Q *.dll *.obj diff --git a/examples/asm_examples/ex_dll_export_thunk/ex.asm b/examples/asm_examples/ex_dll_export_thunk/ex.asm new file mode 100644 index 00000000..4c7f4b11 --- /dev/null +++ b/examples/asm_examples/ex_dll_export_thunk/ex.asm @@ -0,0 +1,33 @@ +INCLUDELIB libcmt + +EXTERN __ImageBase:BYTE + +EXTRN puts:PROC + +.CODE + + +print_ok1 PROC EXPORT + JMP print_ok +print_ok1 ENDP + +print_ok2 PROC EXPORT + JMP print_ok +print_ok2 ENDP + +print_ok3 PROC EXPORT + JMP print_ok +print_ok3 ENDP + + +print_ok: + LEA RCX, OFFSET $OK + CALL puts + +exit: + RET 0 +.DATA +$OK DB "ok", 00H + + +END diff --git a/src/datalog/basic_function_inference.dl b/src/datalog/basic_function_inference.dl index 2e0f8d38..b40e8605 100644 --- a/src/datalog/basic_function_inference.dl +++ b/src/datalog/basic_function_inference.dl @@ -52,6 +52,7 @@ function_entry_initial(EA):- function_symbol(EA,_); start_function(EA); loaded_section(EA,_,_); + pe_export_entry(EA,_,_); // TODO: Fold pc_load_call into generic arch component. direct_call(EA_caller,EA), code(EA_caller), !pc_load_call(EA_caller,EA) ). diff --git a/tests/function_inference_test.py b/tests/function_inference_test.py index 42c1b53f..f0821b77 100644 --- a/tests/function_inference_test.py +++ b/tests/function_inference_test.py @@ -1,11 +1,14 @@ import platform import unittest -from disassemble_reassemble_check import compile, disassemble, cd +from disassemble_reassemble_check import compile, disassemble, cd, make import gtirb +import os +import subprocess from pathlib import Path ex_dir = Path("./examples/") +ex_asm_dir = ex_dir / "asm_examples" class TestFunctionInference(unittest.TestCase): @@ -386,5 +389,30 @@ def test_functions_virtualDispatch_clang(self): ) +class PEFunctionInferenceTests(unittest.TestCase): + @unittest.skipUnless( + platform.system() == "Windows" + and os.environ.get("VSCMD_ARG_TGT_ARCH") == "x64", + "This test is Windows (x64) only.", + ) + def test_code_exports_are_functions(self): + """ + Test that any code that is exported is + considered as a function entry. + """ + with cd(ex_asm_dir / "ex_dll_export_thunk"): + subprocess.run(make("all"), stdout=subprocess.DEVNULL) + self.assertTrue(disassemble("ex.dll", format="--ir")[0]) + ir = gtirb.IR.load_protobuf("ex.dll.gtirb") + module = ir.modules[0] + functionNames = { + sym.name + for sym in module.aux_data["functionNames"].data.values() + } + self.assertIn("print_ok1", functionNames) + self.assertIn("print_ok2", functionNames) + self.assertIn("print_ok3", functionNames) + + if __name__ == "__main__": unittest.main()