Merge remote-tracking branch 'origin/caheckman_DecompilerTestFramework'

This commit is contained in:
ghidra1 2021-02-02 12:06:04 -05:00
commit 09478efc2a
62 changed files with 3126 additions and 1204 deletions

View file

@ -11,6 +11,25 @@ src/decompile/build.gradle||GHIDRA||||END|
src/decompile/cpp/.gitignore||GHIDRA||||END| src/decompile/cpp/.gitignore||GHIDRA||||END|
src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END| src/decompile/cpp/Doxyfile||GHIDRA|||Most of this file is autogenerated by doxygen which falls under the GPL - output from GPL products are NOT GPL! - mjbell4|END|
src/decompile/cpp/Makefile||GHIDRA||||END| src/decompile/cpp/Makefile||GHIDRA||||END|
src/decompile/datatests/deadvolatile.xml||GHIDRA||||END|
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
src/decompile/datatests/forloop1.xml||GHIDRA||||END|
src/decompile/datatests/forloop_loaditer.xml||GHIDRA||||END|
src/decompile/datatests/forloop_thruspecial.xml||GHIDRA||||END|
src/decompile/datatests/forloop_varused.xml||GHIDRA||||END|
src/decompile/datatests/forloop_withskip.xml||GHIDRA||||END|
src/decompile/datatests/loopcomment.xml||GHIDRA||||END|
src/decompile/datatests/namespace.xml||GHIDRA||||END|
src/decompile/datatests/nestedoffset.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_alias.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_globcall.xml||GHIDRA||||END|
src/decompile/datatests/noforloop_iterused.xml||GHIDRA||||END|
src/decompile/datatests/offsetarray.xml||GHIDRA||||END|
src/decompile/datatests/promotecompare.xml||GHIDRA||||END|
src/decompile/datatests/readvolatile.xml||GHIDRA||||END|
src/decompile/datatests/threedim.xml||GHIDRA||||END|
src/decompile/datatests/twodim.xml||GHIDRA||||END|
src/decompile/datatests/wayoffarray.xml||GHIDRA||||END|
src/main/doc/cspec.xml||GHIDRA||||END| src/main/doc/cspec.xml||GHIDRA||||END|
src/main/doc/cspec_html.xsl||GHIDRA||||END| src/main/doc/cspec_html.xsl||GHIDRA||||END|
src/main/doc/decompileplugin.xml||GHIDRA||||END| src/main/doc/decompileplugin.xml||GHIDRA||||END|

View file

@ -1,288 +1,146 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage"> <?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<storageModule moduleId="org.eclipse.cdt.core.settings"> <cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1693333286">
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1693333286" moduleId="org.eclipse.cdt.core.settings" name="Default">
<cconfiguration id="cdt.managedbuild.toolchain.gnu.base.1693333286"> <externalSettings/>
<extensions>
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.base.1693333286" moduleId="org.eclipse.cdt.core.settings" name="Default"> <extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<externalSettings/> <extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extensions> <extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/> </extensions>
</storageModule>
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> <storageModule moduleId="cdtBuildSystem" version="4.0.0">
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1693333286" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/> <folderInfo id="cdt.managedbuild.toolchain.gnu.base.1693333286.895166479" name="/" resourcePath="">
<toolChain id="cdt.managedbuild.toolchain.gnu.base.1966692717" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.base">
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.2081261468" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/>
<builder buildPath="${workspace_loc:/_Decompiler}/cpp" id="cdt.managedbuild.target.gnu.builder.base.2048731974" incrementalBuildTarget="ghidra_opt" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.target.gnu.builder.base"/>
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> <tool id="cdt.managedbuild.tool.gnu.archiver.base.1325849601" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.233123430" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base">
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/> <inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1087598226" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
</tool>
</extensions> <tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1211851151" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base">
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.12359898" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
</storageModule> </tool>
<tool id="cdt.managedbuild.tool.gnu.c.linker.base.1810518227" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/>
<storageModule moduleId="cdtBuildSystem" version="4.0.0"> <tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1320265924" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base">
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.361769658" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
<configuration artifactName="${ProjName}" buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.base.1693333286" name="Default" parent="org.eclipse.cdt.build.core.emptycfg"> <additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
<folderInfo id="cdt.managedbuild.toolchain.gnu.base.1693333286.895166479" name="/" resourcePath=""> </inputType>
</tool>
<toolChain id="cdt.managedbuild.toolchain.gnu.base.1966692717" name="Linux GCC" superClass="cdt.managedbuild.toolchain.gnu.base"> <tool id="cdt.managedbuild.tool.gnu.assembler.base.74027553" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1774124767" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.target.gnu.platform.base.2081261468" name="Debug Platform" osList="linux,hpux,aix,qnx" superClass="cdt.managedbuild.target.gnu.platform.base"/> </tool>
</toolChain>
<builder buildPath="${workspace_loc:/_Decompiler}/cpp" id="cdt.managedbuild.target.gnu.builder.base.2048731974" incrementalBuildTarget="ghidra_opt" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelBuildOn="true" parallelizationNumber="optimal" superClass="cdt.managedbuild.target.gnu.builder.base"/> </folderInfo>
<sourceEntries>
<tool id="cdt.managedbuild.tool.gnu.archiver.base.1325849601" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.base"/> <entry excluding="unittests|extcpp" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="extcpp"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.base.233123430" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.base"> <entry flags="VALUE_WORKSPACE_PATH" kind="sourcePath" name="unittests"/>
</sourceEntries>
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1087598226" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/> </configuration>
</storageModule>
</tool> <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
<tool id="cdt.managedbuild.tool.gnu.c.compiler.base.1211851151" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.base"> </storageModule>
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.12359898" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/> <project id="_Decompiler.null.1084391757" name="_Decompiler"/>
</storageModule>
</tool> <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
<storageModule moduleId="refreshScope" versionNumber="2">
<tool id="cdt.managedbuild.tool.gnu.c.linker.base.1810518227" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.base"/> <configuration configurationName="Default">
<resource resourceType="PROJECT" workspacePath="/_Decompiler"/>
<tool id="cdt.managedbuild.tool.gnu.cpp.linker.base.1320265924" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.base"> </configuration>
</storageModule>
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.361769658" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input"> <storageModule moduleId="scannerConfiguration">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/> <scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1693333286;cdt.managedbuild.toolchain.gnu.base.1693333286.895166479;cdt.managedbuild.tool.gnu.c.compiler.base.1211851151;cdt.managedbuild.tool.gnu.c.compiler.input.12359898">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
<additionalInput kind="additionalinput" paths="$(LIBS)"/> </scannerConfigBuildInfo>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1693333286;cdt.managedbuild.toolchain.gnu.base.1693333286.895166479;cdt.managedbuild.tool.gnu.cpp.compiler.base.233123430;cdt.managedbuild.tool.gnu.cpp.compiler.input.1087598226">
</inputType> <autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
</scannerConfigBuildInfo>
</tool> </storageModule>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets">
<tool id="cdt.managedbuild.tool.gnu.assembler.base.74027553" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.base"> <buildTargets>
<target name="reallyclean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.1774124767" superClass="cdt.managedbuild.tool.gnu.assembler.input"/> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
</tool> <buildTarget>reallyclean</buildTarget>
<stopOnError>true</stopOnError>
</toolChain> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</folderInfo> </target>
<target name="sleigh_dbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<sourceEntries> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<entry excluding="extcpp" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> <buildTarget>sleigh_dbg</buildTarget>
<stopOnError>true</stopOnError>
<entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="extcpp"/> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</sourceEntries> </target>
<target name="ghidra_dbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
</configuration> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
</storageModule> <buildTarget>ghidra_dbg</buildTarget>
<stopOnError>true</stopOnError>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</cconfiguration> </target>
<target name="decomp_dbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
</storageModule> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<storageModule moduleId="cdtBuildSystem" version="4.0.0"> <buildTarget>decomp_dbg</buildTarget>
<stopOnError>true</stopOnError>
<project id="_Decompiler.null.1084391757" name="_Decompiler"/> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</storageModule> </target>
<target name="ghidra_opt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<storageModule moduleId="refreshScope" versionNumber="2"> <buildTarget>ghidra_opt</buildTarget>
<stopOnError>true</stopOnError>
<configuration configurationName="Default"> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
<resource resourceType="PROJECT" workspacePath="/_Decompiler"/> </target>
<target name="decomp_opt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
</configuration> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
</storageModule> <buildTarget>decomp_opt</buildTarget>
<stopOnError>true</stopOnError>
<storageModule moduleId="scannerConfiguration"> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> </target>
<target name="sleigh_opt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1693333286;cdt.managedbuild.toolchain.gnu.base.1693333286.895166479;cdt.managedbuild.tool.gnu.c.compiler.base.1211851151;cdt.managedbuild.tool.gnu.c.compiler.input.12359898"> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> <buildTarget>sleigh_opt</buildTarget>
<stopOnError>true</stopOnError>
</scannerConfigBuildInfo> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.base.1693333286;cdt.managedbuild.toolchain.gnu.base.1693333286.895166479;cdt.managedbuild.tool.gnu.cpp.compiler.base.233123430;cdt.managedbuild.tool.gnu.cpp.compiler.input.1087598226"> </target>
<target name="install_ghidradbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
</scannerConfigBuildInfo> <buildTarget>install_ghidradbg</buildTarget>
<stopOnError>true</stopOnError>
</storageModule> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
<storageModule moduleId="org.eclipse.cdt.make.core.buildtargets"> </target>
<target name="install_ghidraopt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildTargets> <buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<target name="reallyclean" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder"> <buildTarget>install_ghidraopt</buildTarget>
<stopOnError>true</stopOnError>
<buildCommand>make</buildCommand> <useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
<buildArguments>-j8</buildArguments> </target>
</buildTargets>
<buildTarget>reallyclean</buildTarget> </storageModule>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="sleigh_dbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>sleigh_dbg</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="ghidra_dbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>ghidra_dbg</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="decomp_dbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>decomp_dbg</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="ghidra_opt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>ghidra_opt</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="decomp_opt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>decomp_opt</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="sleigh_opt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>sleigh_opt</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="install_ghidradbg" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>install_ghidradbg</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
<target name="install_ghidraopt" path="" targetID="org.eclipse.cdt.build.MakeTargetBuilder">
<buildCommand>make</buildCommand>
<buildArguments>-j8</buildArguments>
<buildTarget>install_ghidraopt</buildTarget>
<stopOnError>true</stopOnError>
<useDefaultCommand>true</useDefaultCommand>
<runAllBuilders>true</runAllBuilders>
</target>
</buildTargets>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
</cproject> </cproject>

View file

@ -457,7 +457,7 @@ RECURSIVE = NO
# excluded from the INPUT source files. This way you can easily exclude a # excluded from the INPUT source files. This way you can easily exclude a
# subdirectory from a directory tree whose root is specified with the INPUT tag. # subdirectory from a directory tree whose root is specified with the INPUT tag.
EXCLUDE = unify.hh unify.cc rulecompile.hh rulecompile.cc slgh_compile.hh slgh_compile.hh slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc ifacedecomp.hh ifacedecomp.cc ifaceterm.hh ifaceterm.cc codedata.hh codedata.cc semantics.hh semantics.cc grammar.hh grammar.cc callgraph.hh callgraph.cc filemanage.hh filemanage.cc graph.hh graph.cc interface.hh interface.cc loadimage_bfd.hh loadimage_bfd.cc pcodecompile.cc pcodecompile.hh pcodeparse.hh pcodeparse.cc inject_sleigh.hh inject_sleigh.cc context.hh context.cc consolemain.cc sleighexample.cc xml.cc EXCLUDE = unify.hh unify.cc rulecompile.hh rulecompile.cc slgh_compile.hh slgh_compile.cc slghparse.cc slghparse.hh slghscan.cc slghpattern.hh slghpattern.cc slghpatexpress.hh slghpatexpress.cc slghsymbol.hh slghsymbol.cc codedata.hh codedata.cc semantics.hh semantics.cc grammar.hh grammar.cc callgraph.hh callgraph.cc filemanage.hh filemanage.cc graph.hh graph.cc loadimage_bfd.hh loadimage_bfd.cc pcodecompile.cc pcodecompile.hh pcodeparse.hh pcodeparse.cc inject_sleigh.hh inject_sleigh.cc context.hh context.cc consolemain.cc sleighexample.cc xml.cc double.hh double.cc paramid.hh paramid.cc prefersplit.hh prefersplit.cc
# The EXCLUDE_SYMLINKS tag can be used select whether or not files or # The EXCLUDE_SYMLINKS tag can be used select whether or not files or
# directories that are symbolic links (a Unix filesystem feature) are excluded # directories that are symbolic links (a Unix filesystem feature) are excluded
@ -870,18 +870,6 @@ GENERATE_XML = NO
XML_OUTPUT = xml XML_OUTPUT = xml
# The XML_SCHEMA tag can be used to specify an XML schema,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_SCHEMA =
# The XML_DTD tag can be used to specify an XML DTD,
# which can be used by a validating XML parser to check the
# syntax of the XML files.
XML_DTD =
# If the XML_PROGRAMLISTING tag is set to YES Doxygen will # If the XML_PROGRAMLISTING tag is set to YES Doxygen will
# dump the program listings (including syntax highlighting # dump the program listings (including syntax highlighting
# and cross-referencing information) to the XML output. Note that # and cross-referencing information) to the XML output. Note that

View file

@ -56,6 +56,9 @@ LNK=
# Source files # Source files
ALL_SOURCE= $(wildcard *.cc) ALL_SOURCE= $(wildcard *.cc)
ALL_NAMES=$(subst .cc,,$(ALL_SOURCE)) ALL_NAMES=$(subst .cc,,$(ALL_SOURCE))
UNITTEST_SOURCE= $(wildcard ../unittests/*.cc)
UNITTEST_NAMES=$(subst .cc,,$(UNITTEST_SOURCE))
UNITTEST_STRIP=$(subst ../unittests/,,$(UNITTEST_NAMES))
COREEXT_SOURCE= $(wildcard coreext_*.cc) COREEXT_SOURCE= $(wildcard coreext_*.cc)
COREEXT_NAMES=$(subst .cc,,$(COREEXT_SOURCE)) COREEXT_NAMES=$(subst .cc,,$(COREEXT_SOURCE))
@ -91,7 +94,7 @@ GHIDRA= ghidra_arch inject_ghidra ghidra_translate loadimage_ghidra \
# Additional files specific to the sleigh compiler # Additional files specific to the sleigh compiler
SLACOMP=slgh_compile slghparse slghscan SLACOMP=slgh_compile slghparse slghscan
# Additional special files that should not be considered part of the library # Additional special files that should not be considered part of the library
SPECIAL=consolemain sleighexample test SPECIAL=consolemain sleighexample test testfunction
# Any additional modules for the command line decompiler # Any additional modules for the command line decompiler
EXTRA= $(filter-out $(CORE) $(DECCORE) $(SLEIGH) $(GHIDRA) $(SLACOMP) $(SPECIAL),$(ALL_NAMES)) EXTRA= $(filter-out $(CORE) $(DECCORE) $(SLEIGH) $(GHIDRA) $(SLACOMP) $(SPECIAL),$(ALL_NAMES))
@ -114,8 +117,8 @@ COMMANDLINE_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH) consolemain
COMMANDLINE_DEBUG=-DCPUI_DEBUG -D__TERMINAL__ COMMANDLINE_DEBUG=-DCPUI_DEBUG -D__TERMINAL__
COMMANDLINE_OPT=-D__TERMINAL__ COMMANDLINE_OPT=-D__TERMINAL__
TEST_NAMES=$(CORE) $(DECCORE) $(SLEIGH) test TEST_NAMES=$(CORE) $(DECCORE) $(SLEIGH) $(EXTRA) testfunction test
TEST_DEBUG=-D__TERMINAL__ -g -O0 TEST_DEBUG=-D__TERMINAL__
GHIDRA_NAMES=$(CORE) $(DECCORE) $(GHIDRA) GHIDRA_NAMES=$(CORE) $(DECCORE) $(GHIDRA)
GHIDRA_NAMES_DBG=$(GHIDRA_NAMES) callgraph ifacedecomp ifaceterm interface GHIDRA_NAMES_DBG=$(GHIDRA_NAMES) callgraph ifacedecomp ifaceterm interface
@ -136,7 +139,7 @@ LIBDECOMP_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH)
# object file macros # object file macros
COMMANDLINE_DBG_OBJS=$(COMMANDLINE_NAMES:%=com_dbg/%.o) COMMANDLINE_DBG_OBJS=$(COMMANDLINE_NAMES:%=com_dbg/%.o)
COMMANDLINE_OPT_OBJS=$(COMMANDLINE_NAMES:%=com_opt/%.o) COMMANDLINE_OPT_OBJS=$(COMMANDLINE_NAMES:%=com_opt/%.o)
TEST_DEBUG_OBJS=$(TEST_NAMES:%=test_dbg/%.o) TEST_DEBUG_OBJS=$(TEST_NAMES:%=test_dbg/%.o) $(UNITTEST_STRIP:%=test_dbg/%.o)
GHIDRA_DBG_OBJS=$(GHIDRA_NAMES_DBG:%=ghi_dbg/%.o) GHIDRA_DBG_OBJS=$(GHIDRA_NAMES_DBG:%=ghi_dbg/%.o)
GHIDRA_OPT_OBJS=$(GHIDRA_NAMES:%=ghi_opt/%.o) GHIDRA_OPT_OBJS=$(GHIDRA_NAMES:%=ghi_opt/%.o)
SLEIGH_DBG_OBJS=$(SLEIGH_NAMES:%=sla_dbg/%.o) SLEIGH_DBG_OBJS=$(SLEIGH_NAMES:%=sla_dbg/%.o)
@ -214,7 +217,9 @@ com_dbg/%.o: %.cc
com_opt/%.o: %.cc com_opt/%.o: %.cc
$(CXX) $(ARCH_TYPE) -c $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(COMMANDLINE_OPT) $< -o $@ $(CXX) $(ARCH_TYPE) -c $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(COMMANDLINE_OPT) $< -o $@
test_dbg/%.o: %.cc test_dbg/%.o: %.cc
$(CXX) $(ARCH_TYPE) -c $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) $< -o $@ $(CXX) $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) $< -o $@
test_dbg/%.o: ../unittests/%.cc
$(CXX) -I. $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) $< -o $@
ghi_dbg/%.o: %.cc ghi_dbg/%.o: %.cc
$(CXX) $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(GHIDRA_DEBUG) $< -o $@ $(CXX) $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(GHIDRA_DEBUG) $< -o $@
ghi_opt/%.o: %.cc ghi_opt/%.o: %.cc
@ -248,7 +253,7 @@ decomp_opt: $(COMMANDLINE_OPT_OBJS)
$(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o decomp_opt $(COMMANDLINE_OPT_OBJS) $(BFDLIB) $(LNK) $(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o decomp_opt $(COMMANDLINE_OPT_OBJS) $(BFDLIB) $(LNK)
ghidra_test_dbg: $(TEST_DEBUG_OBJS) ghidra_test_dbg: $(TEST_DEBUG_OBJS)
$(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o ghidra_test_dbg $(TEST_DEBUG_OBJS) $(BFDLIB) $(LNK) $(CXX) $(DBG_CXXFLAGS) $(ARCH_TYPE) -o ghidra_test_dbg $(TEST_DEBUG_OBJS) $(BFDLIB) $(LNK)
test: ghidra_test_dbg test: ghidra_test_dbg
./ghidra_test_dbg ./ghidra_test_dbg
@ -339,10 +344,10 @@ com_opt/depend: $(COMMANDLINE_NAMES:%=%.cc)
sed 's,\(.*\)\.o[ :]*,com_opt/\1.o $@ : ,g' < $@.$$$$ > $@; \ sed 's,\(.*\)\.o[ :]*,com_opt/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$ rm -f $@.$$$$
test_dbg/depend: $(TEST_NAMES:%=%.cc) test_dbg/depend: $(TEST_NAMES:%=%.cc) $(UNITTEST_NAMES:%=%.cc)
mkdir -p test_dbg mkdir -p test_dbg
@set -e; rm -f $@; \ @set -e; rm -f $@; \
$(CXX) -MM $(TEST_DEBUG) $^ > $@.$$$$; \ $(CXX) -I. -MM $(TEST_DEBUG) $^ > $@.$$$$; \
sed 's,\(.*\)\.o[ :]*,test_dbg/\1.o $@ : ,g' < $@.$$$$ > $@; \ sed 's,\(.*\)\.o[ :]*,test_dbg/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$ rm -f $@.$$$$

View file

@ -64,6 +64,20 @@ ArchitectureCapability *ArchitectureCapability::findCapability(Document *doc)
return (ArchitectureCapability *)0; return (ArchitectureCapability *)0;
} }
/// Return the ArchitectureCapability object with the matching name
/// \param name is the name to match
/// \return the ArchitectureCapability or null if no match is found
ArchitectureCapability *ArchitectureCapability::getCapability(const string &name)
{
for(int4 i=0;i<thelist.size();++i) {
ArchitectureCapability *res = thelist[i];
if (res->getName() == name)
return res;
}
return (ArchitectureCapability *)0;
}
/// Modify order that extensions are searched, to effect which gets a chance /// Modify order that extensions are searched, to effect which gets a chance
/// to run first. /// to run first.
/// Right now all we need to do is make sure the raw architecture comes last /// Right now all we need to do is make sure the raw architecture comes last

View file

@ -104,6 +104,7 @@ public:
static ArchitectureCapability *findCapability(const string &filename); ///< Find an extension to process a file static ArchitectureCapability *findCapability(const string &filename); ///< Find an extension to process a file
static ArchitectureCapability *findCapability(Document *doc); ///< Find an extension to process an XML document static ArchitectureCapability *findCapability(Document *doc); ///< Find an extension to process an XML document
static ArchitectureCapability *getCapability(const string &name); ///< Get a capability by name
static void sortCapabilities(void); ///< Sort extensions static void sortCapabilities(void); ///< Sort extensions
static uint4 getMajorVersion(void) { return majorversion; } ///< Get \e major decompiler version static uint4 getMajorVersion(void) { return majorversion; } ///< Get \e major decompiler version
static uint4 getMinorVersion(void) { return minorversion; } ///< Get \e minor decompiler version static uint4 getMinorVersion(void) { return minorversion; } ///< Get \e minor decompiler version

View file

@ -574,7 +574,10 @@ int4 FlowBlock::getOutIndex(const FlowBlock *bl) const
void FlowBlock::printHeader(ostream &s) const void FlowBlock::printHeader(ostream &s) const
{ {
s << dec << index << ' ' << getStart() << '-' << getStop(); s << dec << index;
if (!getStart().isInvalid() && !getStop().isInvalid()) {
s << ' ' << getStart() << '-' << getStop();
}
} }
/// Recursively print out the hierarchical structure of \b this FlowBlock. /// Recursively print out the hierarchical structure of \b this FlowBlock.

View file

@ -597,8 +597,8 @@ class BlockWhileDo : public BlockGraph {
bool testIterateForm(void) const; ///< Return \b false if the iterate statement is of an unacceptable form bool testIterateForm(void) const; ///< Return \b false if the iterate statement is of an unacceptable form
public: public:
BlockWhileDo(void) { initializeOp = (PcodeOp *)0; iterateOp = (PcodeOp *)0; loopDef = (PcodeOp *)0; } ///< Constructor BlockWhileDo(void) { initializeOp = (PcodeOp *)0; iterateOp = (PcodeOp *)0; loopDef = (PcodeOp *)0; } ///< Constructor
PcodeOp *getInitializeOp(void) const { return initializeOp; } PcodeOp *getInitializeOp(void) const { return initializeOp; } ///< Get root of initialize statement or null
PcodeOp *getIterateOp(void) const { return iterateOp; } PcodeOp *getIterateOp(void) const { return iterateOp; } ///< Get root of iterate statement or null
bool hasOverflowSyntax(void) const { return ((getFlags() & f_whiledo_overflow)!=0); } ///< Does \b this require overflow syntax bool hasOverflowSyntax(void) const { return ((getFlags() & f_whiledo_overflow)!=0); } ///< Does \b this require overflow syntax
void setOverflowSyntax(void) { setFlag(f_whiledo_overflow); } ///< Set that \b this requires overflow syntax void setOverflowSyntax(void) { setFlag(f_whiledo_overflow); } ///< Set that \b this requires overflow syntax
virtual block_type getType(void) const { return t_whiledo; } virtual block_type getType(void) const { return t_whiledo; }

View file

@ -54,8 +54,8 @@ public:
}; };
Comment(uint4 tp,const Address &fad,const Address &ad,int4 uq,const string &txt); ///< Constructor Comment(uint4 tp,const Address &fad,const Address &ad,int4 uq,const string &txt); ///< Constructor
Comment(void) {} ///< Constructor for use with restoreXml Comment(void) {} ///< Constructor for use with restoreXml
void setEmitted(bool val) const { emitted = val; } void setEmitted(bool val) const { emitted = val; } ///< Mark that \b this comment has been emitted
bool isEmitted(void) const { return emitted; } bool isEmitted(void) const { return emitted; } ///< Return \b true if \b this comment is already emitted
uint4 getType(void) const { return type; } ///< Get the properties associated with the comment uint4 getType(void) const { return type; } ///< Get the properties associated with the comment
const Address &getFuncAddr(void) const { return funcaddr; } ///< Get the address of the function containing the comment const Address &getFuncAddr(void) const { return funcaddr; } ///< Get the address of the function containing the comment
const Address &getAddr(void) const { return addr; } ///< Get the address to which the instruction is attached const Address &getAddr(void) const { return addr; } ///< Get the address to which the instruction is attached

View file

@ -3477,9 +3477,10 @@ bool ActionDeadCode::isEventualConstant(Varnode *vn,int4 addCount,int4 loadCount
/// \brief Check if there are any unconsumed LOADs that may be from volatile addresses. /// \brief Check if there are any unconsumed LOADs that may be from volatile addresses.
/// ///
/// It may be too early to remove certain LOAD operations even though their result isn't /// It may be too early to remove certain LOAD operations even though their result isn't
/// consumed because it be of a volatile address with side effects. If a LOAD meets this /// consumed because it may be of a volatile address with side effects. If a LOAD meets this
/// criteria, it is added to the worklist and \b true is returned. /// criteria, it is added to the worklist and \b true is returned.
/// \param data is the function being analyzed /// \param data is the function being analyzed
/// \param worklist is the container of consumed Varnodes to further process
/// \return \b true if there was at least one LOAD added to the worklist /// \return \b true if there was at least one LOAD added to the worklist
bool ActionDeadCode::lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist) bool ActionDeadCode::lastChanceLoad(Funcdata &data,vector<Varnode *> &worklist)

View file

@ -2817,7 +2817,7 @@ void Database::fillResolve(Scope *scope)
/// Initialize a new symbol table, with no initial scopes or symbols. /// Initialize a new symbol table, with no initial scopes or symbols.
/// \param g is the Architecture that owns the symbol table /// \param g is the Architecture that owns the symbol table
/// \param isByName is \b true if scope ids are calculated as a hash of the scope name. /// \param idByName is \b true if scope ids are calculated as a hash of the scope name.
Database::Database(Architecture *g,bool idByName) Database::Database(Architecture *g,bool idByName)
{ {

View file

@ -19,6 +19,7 @@
/// \param nm is the (base) name of the function /// \param nm is the (base) name of the function
/// \param scope is Symbol scope associated with the function /// \param scope is Symbol scope associated with the function
/// \param addr is the entry address for the function /// \param addr is the entry address for the function
/// \param sym is the symbol representing the function
/// \param sz is the number of bytes (of code) in the function body /// \param sz is the number of bytes (of code) in the function body
Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz) Funcdata::Funcdata(const string &nm,Scope *scope,const Address &addr,FunctionSymbol *sym,int4 sz)
: baseaddr(addr), : baseaddr(addr),

View file

@ -249,7 +249,6 @@ public:
int4 numCalls(void) const { return qlst.size(); } ///< Get the number of calls made by \b this function int4 numCalls(void) const { return qlst.size(); } ///< Get the number of calls made by \b this function
FuncCallSpecs *getCallSpecs(int4 i) const { return qlst[i]; } ///< Get the i-th call specification FuncCallSpecs *getCallSpecs(int4 i) const { return qlst[i]; } ///< Get the i-th call specification
FuncCallSpecs *getCallSpecs(const PcodeOp *op) const; ///< Get the call specification associated with a CALL op FuncCallSpecs *getCallSpecs(const PcodeOp *op) const; ///< Get the call specification associated with a CALL op
void updateOpFromSpec(FuncCallSpecs *fc);
int4 fillinExtrapop(void); ///< Recover and return the \e extrapop for this function int4 fillinExtrapop(void); ///< Recover and return the \e extrapop for this function
// Varnode routines // Varnode routines

View file

@ -291,7 +291,7 @@ void Funcdata::destroyVarnode(Varnode *vn)
/// Check if the given storage range is a potential laned register. /// Check if the given storage range is a potential laned register.
/// If so, record the storage with the matching laned register record. /// If so, record the storage with the matching laned register record.
/// \param s is the size of the storage range in bytes /// \param size is the size of the storage range in bytes
/// \param addr is the starting address of the storage range /// \param addr is the starting address of the storage range
void Funcdata::checkForLanedRegister(int4 size,const Address &addr) void Funcdata::checkForLanedRegister(int4 size,const Address &addr)

View file

@ -690,6 +690,18 @@ void ArchitectureGhidra::getBytes(uint1 *buf,int4 size,const Address &inaddr)
readResponseEnd(sin); readResponseEnd(sin);
} }
/// \brief Get string data at a specific address
///
/// The data is always returned as a sequence of bytes in UTF-8 format. The in-memory form of
/// the string may be different than UTF-8 but is always translated into UTF-8 by this method.
/// The caller can inform the in-memory format of the string by specifying a specific string
/// data-type. A maximum number of bytes to return is specified. If this is exceeded, a boolean
/// reference is set to \b true.
/// \param buffer will hold the string bytes in UTF-8 format
/// \param addr is program Address that holds the string data in memory
/// \param ct is string data-type expected
/// \param maxBytes is the maximum number of bytes to return
/// \param isTrunc is the boolean reference indicating whether the data is truncated
void ArchitectureGhidra::getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc) void ArchitectureGhidra::getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc)
{ {

View file

@ -127,7 +127,7 @@ public:
bool getSendParamMeasures(void) const { return sendParamMeasures; } ///< Get the current setting for emitting parameter info bool getSendParamMeasures(void) const { return sendParamMeasures; } ///< Get the current setting for emitting parameter info
virtual void getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc); void getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes,bool &isTrunc);
virtual void printMessage(const string &message) const; virtual void printMessage(const string &message) const;
static void segvHandler(int4 sig); ///< Handler for a segment violation (SIGSEGV) signal static void segvHandler(int4 sig); ///< Handler for a segment violation (SIGSEGV) signal

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
// Interface to the decompilation routines /// \file ifacedecomp.hh
/// \brief Console interface commands for the decompiler engine
#ifndef __IFACE_DECOMP__ #ifndef __IFACE_DECOMP__
#define __IFACE_DECOMP__ #define __IFACE_DECOMP__
@ -27,20 +28,22 @@
#include "rulecompile.hh" #include "rulecompile.hh"
#endif #endif
/// \brief Interface capability point for all decompiler commands
class IfaceDecompCapability : public IfaceCapability { class IfaceDecompCapability : public IfaceCapability {
static IfaceDecompCapability ifaceDecompCapability; // Singleton instance static IfaceDecompCapability ifaceDecompCapability; ///< Singleton instance
IfaceDecompCapability(void); // Singleton IfaceDecompCapability(void); ///< Singleton constructor
IfaceDecompCapability(const IfaceDecompCapability &op2); // Not implemented IfaceDecompCapability(const IfaceDecompCapability &op2); ///< Not implemented
IfaceDecompCapability &operator=(const IfaceDecompCapability &op2); // Not implemented IfaceDecompCapability &operator=(const IfaceDecompCapability &op2); ///< Not implemented
public: public:
virtual void registerCommands(IfaceStatus *status); virtual void registerCommands(IfaceStatus *status);
}; };
/// \brief Common data shared by decompiler commands
class IfaceDecompData : public IfaceData { class IfaceDecompData : public IfaceData {
public: public:
Funcdata *fd; // Current function data Funcdata *fd; ///< Current function active in the console
Architecture *conf; Architecture *conf; ///< Current architecture/program active in the console
CallGraph *cgraph; CallGraph *cgraph; ///< Call-graph information for the program
map<Funcdata*,PrototypePieces> prototypePieces; map<Funcdata*,PrototypePieces> prototypePieces;
void storePrototypePieces( Funcdata *fd_in, PrototypePieces pp_in ) { prototypePieces.insert(pair<Funcdata*,PrototypePieces>(fd_in,pp_in)); } void storePrototypePieces( Funcdata *fd_in, PrototypePieces pp_in ) { prototypePieces.insert(pair<Funcdata*,PrototypePieces>(fd_in,pp_in)); }
@ -52,18 +55,24 @@ public:
#ifdef OPACTION_DEBUG #ifdef OPACTION_DEBUG
bool jumptabledebug; bool jumptabledebug;
#endif #endif
IfaceDecompData(void); IfaceDecompData(void); ///< Constructor
virtual ~IfaceDecompData(void); virtual ~IfaceDecompData(void);
void allocateCallGraph(void); void allocateCallGraph(void); ///< Allocate the call-graph object
void abortFunction(ostream &s); void abortFunction(ostream &s); ///< Clear references to current function
void clearArchitecture(void); void clearArchitecture(void); ///< Free all resources for the current architecture/program
void followFlow(ostream &s,int4 size);
Varnode *readVarnode(istream &s); ///< Read a varnode from the given stream
}; };
/// \brief Disassembly emitter that prints to a console stream
///
/// An instruction is printed to a stream simply, as an address
/// followed by the mnemonic and then column aligned operands.
class IfaceAssemblyEmit : public AssemblyEmit { class IfaceAssemblyEmit : public AssemblyEmit {
int4 mnemonicpad; // How much to pad the mnemonic int4 mnemonicpad; ///< How much to pad the mnemonic
ostream *s; ostream *s; ///< The current stream to write to
public: public:
IfaceAssemblyEmit(ostream *val,int4 mp) { s = val; mnemonicpad=mp; } IfaceAssemblyEmit(ostream *val,int4 mp) { s = val; mnemonicpad=mp; } ///< Constructor
virtual void dump(const Address &addr,const string &mnem,const string &body) { virtual void dump(const Address &addr,const string &mnem,const string &body) {
addr.printRaw(*s); addr.printRaw(*s);
*s << ": " << mnem; *s << ": " << mnem;
@ -72,22 +81,31 @@ public:
} }
}; };
extern void execute(IfaceStatus *status,IfaceDecompData *dcp); extern void execute(IfaceStatus *status,IfaceDecompData *dcp); ///< Execute one command for the console
extern void mainloop(IfaceStatus *status); extern void mainloop(IfaceStatus *status); ///< Execute commands as they become available
/// \brief Root class for all decompiler specific commands
///
/// Commands share the data object IfaceDecompData and are capable of
/// iterating over all functions in the program/architecture.
class IfaceDecompCommand : public IfaceCommand { class IfaceDecompCommand : public IfaceCommand {
protected: protected:
IfaceStatus *status; IfaceStatus *status; ///< The console owning \b this command
IfaceDecompData *dcp; IfaceDecompData *dcp; ///< Data common to decompiler commands
void iterateScopesRecursive(Scope *scope); void iterateScopesRecursive(Scope *scope); ///< Iterate recursively over all functions in given scope
void iterateFunctionsAddrOrder(Scope *scope); void iterateFunctionsAddrOrder(Scope *scope); ///< Iterate over all functions in a given scope
public: public:
virtual void setData(IfaceStatus *root,IfaceData *data) { status = root; dcp = (IfaceDecompData *)data; } virtual void setData(IfaceStatus *root,IfaceData *data) { status = root; dcp = (IfaceDecompData *)data; }
virtual string getModule(void) const { return "decompile"; } virtual string getModule(void) const { return "decompile"; }
virtual IfaceData *createData(void) { return new IfaceDecompData(); } virtual IfaceData *createData(void) { return new IfaceDecompData(); }
/// \brief Perform the per-function aspect of \b this command.
///
/// \param fd is the particular function to operate on
virtual void iterationCallback(Funcdata *fd) {} virtual void iterationCallback(Funcdata *fd) {}
void iterateFunctionsAddrOrder(void);
void iterateFunctionsLeafOrder(void); void iterateFunctionsAddrOrder(void); ///< Iterate command over all functions in all scopes
void iterateFunctionsLeafOrder(void); ///< Iterate command over all functions in a call-graph traversal
}; };
class IfcSource : public IfaceDecompCommand { class IfcSource : public IfaceDecompCommand {
@ -257,11 +275,6 @@ public:
virtual void execute(istream &s); virtual void execute(istream &s);
}; };
class IfcBreakjump : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPrintTree : public IfaceDecompCommand { class IfcPrintTree : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
@ -282,18 +295,10 @@ public:
virtual void execute(istream &s); virtual void execute(istream &s);
}; };
class IfcParamIDAnalysis : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcPrintParamMeasures : public IfaceDecompCommand { class IfcPrintParamMeasures : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
}; };
class IfcPrintParamMeasuresXml : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
class IfcRename : public IfaceDecompCommand { class IfcRename : public IfaceDecompCommand {
public: public:
@ -403,6 +408,10 @@ public:
class IfcPrintInputs : public IfaceDecompCommand { class IfcPrintInputs : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
static bool nonTrivialUse(Varnode *vn); ///< Check for non-trivial use of given Varnode
static int4 checkRestore(Varnode *vn); ///< Check if a Varnode is \e restored to its original input value
static bool findRestore(Varnode *vn,Funcdata *fd); ///< Check if storage is \e restored
static void print(Funcdata *fd,ostream &s); ///< Print information about function inputs
}; };
class IfcPrintInputsAll : public IfaceDecompCommand { class IfcPrintInputsAll : public IfaceDecompCommand {
@ -465,6 +474,8 @@ class IfcDuplicateHash : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
virtual void iterationCallback(Funcdata *fd); virtual void iterationCallback(Funcdata *fd);
static void check(Funcdata *fd,ostream &s); ///< Check for duplicate hashes in given function
}; };
class IfcCallGraphDump : public IfaceDecompCommand { class IfcCallGraphDump : public IfaceDecompCommand {
@ -474,7 +485,7 @@ public:
class IfcCallGraphBuild : public IfaceDecompCommand { class IfcCallGraphBuild : public IfaceDecompCommand {
protected: protected:
bool quick; bool quick; ///< Set to \b true if a quick analysis is desired
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
virtual void iterationCallback(Funcdata *fd); virtual void iterationCallback(Funcdata *fd);
@ -490,8 +501,6 @@ public:
}; };
class IfcCallGraphList : public IfaceDecompCommand { class IfcCallGraphList : public IfaceDecompCommand {
protected:
bool quick;
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
virtual void iterationCallback(Funcdata *fd); virtual void iterationCallback(Funcdata *fd);
@ -505,6 +514,8 @@ public:
class IfcCallFixup : public IfaceDecompCommand { class IfcCallFixup : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
static void readPcodeSnippet(istream &s,string &name,string &outname,vector<string> &inname,
string &pcodestring);
}; };
class IfcCallOtherFixup : public IfaceDecompCommand { class IfcCallOtherFixup : public IfaceDecompCommand {
@ -557,12 +568,12 @@ class IfcParseRule : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
}; };
#endif
class IfcExperimentalRules : public IfaceDecompCommand { class IfcExperimentalRules : public IfaceDecompCommand {
public: public:
virtual void execute(istream &s); virtual void execute(istream &s);
}; };
#endif
#ifdef OPACTION_DEBUG #ifdef OPACTION_DEBUG
class IfcDebugAction : public IfaceDecompCommand { class IfcDebugAction : public IfaceDecompCommand {
@ -600,6 +611,11 @@ public:
virtual void execute(istream &s); virtual void execute(istream &s);
}; };
class IfcBreakjump : public IfaceDecompCommand {
public:
virtual void execute(istream &s);
};
#endif #endif
#endif #endif

View file

@ -16,8 +16,9 @@
#include "ifaceterm.hh" #include "ifaceterm.hh"
IfaceTerm::IfaceTerm(const string &prmpt,istream &is,ostream &os) IfaceTerm::IfaceTerm(const string &prmpt,istream &is,ostream &os)
: IfaceStatus(prmpt,is,os) : IfaceStatus(prmpt,os)
{ {
sptr = &is;
#ifdef __TERMINAL__ #ifdef __TERMINAL__
struct termios ittypass; struct termios ittypass;
@ -57,9 +58,16 @@ IfaceTerm::~IfaceTerm(void)
#endif #endif
} }
/// Respond to a TAB key press and try to 'complete' any existing tokens.
/// The method is handed the current state of the command-line in a string, and
/// it updates the command-line in place.
///
/// \param line is current command-line and will hold the final completion
/// \param cursor is the current position of the cursor
/// \return the (possibly new) position of the cursor, after completion
int4 IfaceTerm::doCompletion(string &line,int4 cursor) int4 IfaceTerm::doCompletion(string &line,int4 cursor)
{ // Try to complete the current command {
vector<string> fullcommand; vector<string> fullcommand;
istringstream s(line); istringstream s(line);
string tok; string tok;
@ -229,3 +237,28 @@ void IfaceTerm::readLine(string &line)
} while(val != '\n'); } while(val != '\n');
} }
void IfaceTerm::pushScript(const string &filename,const string &newprompt)
{
ifstream *s = new ifstream(filename.c_str());
if (!*s)
throw IfaceParseError("Unable to open script file");
inputstack.push_back(sptr);
sptr = s;
IfaceStatus::pushScript(filename,newprompt);
}
void IfaceTerm::popScript(void)
{
delete sptr;
sptr = inputstack.back();
inputstack.pop_back();
}
bool IfaceTerm::isStreamFinished(void) const
{
if (done||inerror) return true;
return sptr->eof();
}

View file

@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
// Add some terminal capabilities to the command-line interface /// \file ifaceterm.hh
/// \brief Add some terminal capabilities to the command-line interface (IfaceStatus)
#include "interface.hh" #include "interface.hh"
#ifdef __TERMINAL__ #ifdef __TERMINAL__
@ -23,15 +25,26 @@ extern "C" {
} }
#endif #endif
/// \brief Implement the command-line interface on top of a specific input stream
///
/// An initial input stream is provided as the base stream to parse for commands.
/// Additional input streams can be stacked by invoking scripts.
/// If the stream supports it, the stream parser recognizes special command-line editing
/// and completion keys.
class IfaceTerm : public IfaceStatus { class IfaceTerm : public IfaceStatus {
#ifdef __TERMINAL__ #ifdef __TERMINAL__
bool is_terminal; // True if the input stream is a terminal bool is_terminal; ///< True if the input stream is a terminal
int4 ifd; // Underlying file descriptor int4 ifd; ///< Underlying file descriptor
struct termios itty; // Original terminal settings struct termios itty; ///< Original terminal settings
#endif #endif
int4 doCompletion(string &line,int4 cursor); istream *sptr; ///< The base input stream for the interface
vector<istream *> inputstack; ///< Stack of nested input streams
int4 doCompletion(string &line,int4 cursor); ///< 'Complete' the current command line
virtual void readLine(string &line); virtual void readLine(string &line);
public: public:
IfaceTerm(const string &prmpt,istream &is,ostream &os); IfaceTerm(const string &prmpt,istream &is,ostream &os); ///< Constructor
virtual ~IfaceTerm(void); virtual ~IfaceTerm(void);
virtual void pushScript(const string &filename,const string &newprompt);
virtual void popScript(void);
virtual bool isStreamFinished(void) const;
}; };

View file

@ -29,9 +29,12 @@ void IfaceCapability::initialize(void)
thelist.push_back(this); thelist.push_back(this);
} }
/// Allow each capability to register its own commands
///
/// \param status is the command line interface to register commands with
void IfaceCapability::registerAllCommands(IfaceStatus *status) void IfaceCapability::registerAllCommands(IfaceStatus *status)
{ // Allow each capability to register its own commands {
for(uint4 i=0;i<thelist.size();++i) for(uint4 i=0;i<thelist.size();++i)
thelist[i]->registerCommands(status); thelist[i]->registerCommands(status);
} }
@ -114,10 +117,12 @@ bool RemoteSocket::isSocketOpen(void)
#endif #endif
IfaceStatus::IfaceStatus(const string &prmpt,istream &is,ostream &os,int4 mxhist) /// \param prmpt is the base command line prompt
/// \param os is the base stream to write output to
/// \param mxhist is the maximum number of lines to store in history
IfaceStatus::IfaceStatus(const string &prmpt,ostream &os,int4 mxhist)
{ {
sptr = &is;
optr = &os; optr = &os;
fileoptr = optr; // Bulk out, defaults to command line output fileoptr = optr; // Bulk out, defaults to command line output
sorted = false; sorted = false;
@ -129,28 +134,30 @@ IfaceStatus::IfaceStatus(const string &prmpt,istream &is,ostream &os,int4 mxhist
curhistory = 0; curhistory = 0;
} }
/// \brief Provide a new script file to execute, with an associated command prompt
///
/// The script provides a subsidiary input stream to the current stream.
/// Once commands from the script are complete, processing will resume on this stream.
/// \param filename is the name of the file containing the script
/// \param newprompt is the command line prompt
void IfaceStatus::pushScript(const string &filename,const string &newprompt) void IfaceStatus::pushScript(const string &filename,const string &newprompt)
{ // Push new input stream on stack (with new prompt) {
ifstream *s = new ifstream(filename.c_str());
if (!*s)
throw IfaceParseError("Unable to open script file");
inputstack.push_back(sptr);
promptstack.push_back(prompt); promptstack.push_back(prompt);
uint4 flags = 0; uint4 flags = 0;
if (errorisdone) if (errorisdone)
flags |= 1; flags |= 1;
flagstack.push_back(flags); flagstack.push_back(flags);
sptr = s;
prompt = newprompt; prompt = newprompt;
} }
/// \brief Return to processing the parent stream
///
/// The current input stream, as established by a script, is popped from the stack,
/// along with its command prompt, and processing continues with the previous stream.
void IfaceStatus::popScript(void) void IfaceStatus::popScript(void)
{ // Pop the current input stream (and current prompt) {
delete sptr;
sptr = inputstack.back();
inputstack.pop_back();
prompt = promptstack.back(); prompt = promptstack.back();
promptstack.pop_back(); promptstack.pop_back();
uint4 flags = flagstack.back(); uint4 flags = flagstack.back();
@ -162,15 +169,17 @@ void IfaceStatus::popScript(void)
void IfaceStatus::reset(void) void IfaceStatus::reset(void)
{ {
while(!inputstack.empty()) while(!promptstack.empty())
popScript(); popScript();
errorisdone = false; errorisdone = false;
done = false; done = false;
} }
/// The line is saved in a circular history buffer
/// \param line is the command line to save
void IfaceStatus::saveHistory(const string &line) void IfaceStatus::saveHistory(const string &line)
{ // Save line in circular history buffer {
if (history.size() < maxhistory) if (history.size() < maxhistory)
history.push_back(line); history.push_back(line);
else else
@ -180,6 +189,10 @@ void IfaceStatus::saveHistory(const string &line)
curhistory = 0; curhistory = 0;
} }
/// A command line is selected by specifying how many steps in time
/// to go back through the list of successful command lines.
/// \param line will hold the selected command line from history
/// \param i is the number of steps back to go
void IfaceStatus::getHistory(string &line,int4 i) const void IfaceStatus::getHistory(string &line,int4 i) const
{ {
@ -191,9 +204,10 @@ void IfaceStatus::getHistory(string &line,int4 i) const
line = history[i]; line = history[i];
} }
// The last command has failed, decide if we are completely abandoning this stream
void IfaceStatus::evaluateError(void) void IfaceStatus::evaluateError(void)
{ // The last command has failed, decide if we are completely abandoning this stream {
if (errorisdone) { if (errorisdone) {
*optr << "Aborting process" << endl; *optr << "Aborting process" << endl;
inerror = true; inerror = true;
@ -208,6 +222,7 @@ void IfaceStatus::evaluateError(void)
inerror = false; inerror = false;
} }
/// Concatenate a list of tokens into a single string, separated by a space character
void IfaceStatus::wordsToString(string &res,const vector<string> &list) void IfaceStatus::wordsToString(string &res,const vector<string> &list)
{ {
@ -228,7 +243,7 @@ IfaceStatus::~IfaceStatus(void)
((ofstream *)fileoptr)->close(); ((ofstream *)fileoptr)->close();
delete fileoptr; delete fileoptr;
} }
while(!inputstack.empty()) while(!promptstack.empty())
popScript(); popScript();
for(int4 i=0;i<comlist.size();++i) for(int4 i=0;i<comlist.size();++i)
delete comlist[i]; delete comlist[i];
@ -238,13 +253,24 @@ IfaceStatus::~IfaceStatus(void)
delete (*iter).second; delete (*iter).second;
} }
/// \brief Register a command with this interface
///
/// A command object is associated with one or more tokens on the command line.
/// A string containing up to 5 tokens can be associated with the command.
///
/// \param fptr is the IfaceCommand object
/// \param nm1 is the first token representing the command
/// \param nm2 is the second token (or null)
/// \param nm3 is the third token (or null)
/// \param nm4 is the fourth token (or null)
/// \param nm5 is the fifth token (or null)
void IfaceStatus::registerCom(IfaceCommand *fptr,const char *nm1, void IfaceStatus::registerCom(IfaceCommand *fptr,const char *nm1,
const char *nm2, const char *nm2,
const char *nm3, const char *nm3,
const char *nm4, const char *nm4,
const char *nm5) const char *nm5)
{ // Register an interface command {
fptr->addWord(nm1); fptr->addWord(nm1);
if (nm2 != (const char *)0) if (nm2 != (const char *)0)
fptr->addWord(nm2); fptr->addWord(nm2);
@ -270,15 +296,24 @@ void IfaceStatus::registerCom(IfaceCommand *fptr,const char *nm1,
fptr->setData(this,data); // Inform command of its data fptr->setData(this,data); // Inform command of its data
} }
/// Commands (IfaceCommand) are associated with a particular module that has
/// a formal name and a data object associated with it. This method
/// retrieves the module specific data object by name.
/// \param nm is the name of the module
/// \return the IfaceData object or null
IfaceData *IfaceStatus::getData(const string &nm) const IfaceData *IfaceStatus::getData(const string &nm) const
{ // Get data corresponding to the named module {
map<string,IfaceData *>::const_iterator iter = datamap.find(nm); map<string,IfaceData *>::const_iterator iter = datamap.find(nm);
if (iter == datamap.end()) if (iter == datamap.end())
return (IfaceData *)0; return (IfaceData *)0;
return (*iter).second; return (*iter).second;
} }
/// A single command line is read (via readLine) and executed.
/// If the command is successfully executed, the command line is
/// committed to history and \b true is returned.
/// \return \b true if a command successfully executes
bool IfaceStatus::runCommand(void) bool IfaceStatus::runCommand(void)
{ {
@ -318,9 +353,16 @@ bool IfaceStatus::runCommand(void)
return true; // Indicate a command was executed return true; // Indicate a command was executed
} }
void IfaceStatus::restrict(vector<IfaceCommand *>::const_iterator &first, /// \brief Restrict range of possible commands given a list of command line tokens
vector<IfaceCommand *>::const_iterator &last, ///
vector<string> &input) /// Given a set of tokens partially describing a command, provide the most narrow
/// range of IfaceCommand objects that could be referred to.
/// \param first will hold an iterator to the first command in the range
/// \param last will hold an iterator (one after) the last command in the range
/// \param input is the list of command tokens to match on
void IfaceStatus::restrictCom(vector<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::const_iterator &last,
vector<string> &input)
{ {
vector<IfaceCommand *>::const_iterator newfirst,newlast; vector<IfaceCommand *>::const_iterator newfirst,newlast;
@ -356,16 +398,22 @@ static bool maxmatch(string &res,const string &op1,const string &op2)
return true; return true;
} }
/// \brief Expand tokens from the given input stream to a full command
///
/// A range of possible commands is returned. Processing of the stream
/// stops as soon as at least one complete command is recognized.
/// Tokens partially matching a command are expanded to the full command
/// and passed back.
/// \param expand will hold the list of expanded tokens
/// \param s is the input stream tokens are read from
/// \param first will hold the beginning of the matching range of commands
/// \param last will hold the end of the matching range of commands
/// \return the number of matching commands
int4 IfaceStatus::expandCom(vector<string> &expand,istream &s, int4 IfaceStatus::expandCom(vector<string> &expand,istream &s,
vector<IfaceCommand *>::const_iterator &first, vector<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::const_iterator &last) vector<IfaceCommand *>::const_iterator &last)
{ // Expand tokens on stream to full command {
// Return range of possible commands
// If command is complete with extra arguments
// return (dont process) remaining args
// Return number of matching commands
int4 pos; // Which word are we currently expanding int4 pos; // Which word are we currently expanding
string tok; string tok;
bool res; bool res;
@ -395,7 +443,7 @@ int4 IfaceStatus::expandCom(vector<string> &expand,istream &s,
} }
s >> tok; // Get next token s >> tok; // Get next token
expand.push_back(tok); expand.push_back(tok);
restrict(first,last,expand); restrictCom(first,last,expand);
if (first == last) // If subrange is empty, return 0 if (first == last) // If subrange is empty, return 0
return 0; return 0;
res = maxmatch(tok, (*first)->getCommandWord(pos), (*(last-1))->getCommandWord(pos)); res = maxmatch(tok, (*first)->getCommandWord(pos), (*(last-1))->getCommandWord(pos));
@ -412,9 +460,13 @@ void IfaceCommand::addWords(const vector<string> &wordlist)
com.push_back( *iter ); com.push_back( *iter );
} }
/// The commands are ordered lexicographically and alphabetically by
/// the comparing tokens in their respective command line strings
/// \param op2 is the other command to compare with \b this
/// \return -1, 0, 1 if \b this is earlier, equal to, or after to the other command
int4 IfaceCommand::compare(const IfaceCommand &op2) const int4 IfaceCommand::compare(const IfaceCommand &op2) const
{ // Sort command based on names {
int4 res; int4 res;
vector<string>::const_iterator iter1,iter2; vector<string>::const_iterator iter1,iter2;
@ -433,12 +485,15 @@ int4 IfaceCommand::compare(const IfaceCommand &op2) const
return 0; // Never reaches here return 0; // Never reaches here
} }
/// \param res is overwritten with the full command line string
void IfaceCommand::commandString(string &res) const void IfaceCommand::commandString(string &res) const
{ {
IfaceStatus::wordsToString(res,com); IfaceStatus::wordsToString(res,com);
} }
/// \class IfcQuit
/// \brief Quit command to terminate processing from the given interface
void IfcQuit::execute(istream &s) void IfcQuit::execute(istream &s)
{ // Generic quit call back { // Generic quit call back
@ -448,6 +503,8 @@ void IfcQuit::execute(istream &s)
status->done = true; // Set flag to drop out of mainloop status->done = true; // Set flag to drop out of mainloop
} }
/// \class IfcHistory
/// \brief History command to list the most recent successful commands
void IfcHistory::execute(istream &s) void IfcHistory::execute(istream &s)
{ // List most recent command lines { // List most recent command lines
@ -471,6 +528,8 @@ void IfcHistory::execute(istream &s)
} }
} }
/// \class IfcOpenfile
/// \brief Open file command to redirect bulk output to a specific file stream
void IfcOpenfile::execute(istream &s) void IfcOpenfile::execute(istream &s)
{ {
@ -491,6 +550,8 @@ void IfcOpenfile::execute(istream &s)
} }
} }
/// \class IfcOpenfileAppend
/// \brief Open file command directing bulk output to be appended to a specific file
void IfcOpenfileAppend::execute(istream &s) void IfcOpenfileAppend::execute(istream &s)
{ {
@ -511,6 +572,10 @@ void IfcOpenfileAppend::execute(istream &s)
} }
} }
/// \class IfcClosefile
/// \brief Close command, closing the current bulk output file.
///
/// Subsequent bulk output is redirected to the basic interface output stream
void IfcClosefile::execute(istream &s) void IfcClosefile::execute(istream &s)
{ {
@ -521,6 +586,8 @@ void IfcClosefile::execute(istream &s)
status->fileoptr = status->optr; status->fileoptr = status->optr;
} }
/// \class IfcEcho
/// \brief Echo command to echo the current command line to the bulk output stream
void IfcEcho::execute(istream &s) void IfcEcho::execute(istream &s)
{ // Echo command line to fileoptr { // Echo command line to fileoptr

View file

@ -13,23 +13,8 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
// Very generic command line executor class: IfaceStatus /// \file interface.hh
// A new class instance derived from IfaceCommand is attached to a command line via registerCom /// \brief Classes and utilities for a \e generic command-line interface
// i.e.
// IfaceStatus stat(cin,cout);
// stat.registerCom(new IfcQuit(),"quit");
// stat.registerCom(new IfcOpenfileAppend(),"openfile","append");
// stat.mainloop();
// Command line processing is started with mainloop, which prints a
// prompt set with setprompt, allows bash style command line editing, including
// command completion and history, and executes the corresponding IfaceCommand.execute callback.
// Command words only have to match enough to disambiguate it from other commands.
// Custom history size can be passed in constructor to IfaceStatus.
// Applications should inherit from base class IfaceStatus in order
// to get custom data into IfaceCommand callbacks and to redefine
// the virtual function execute for custom error handling.
#ifndef __INTERFACE__ #ifndef __INTERFACE__
#define __INTERFACE__ #define __INTERFACE__
@ -70,43 +55,87 @@ public:
#endif #endif
/// \brief An exception specific to the command line interface
struct IfaceError { struct IfaceError {
string explain; // Explanatory string string explain; ///< Explanatory string
IfaceError(const string &s) { explain = s; } IfaceError(const string &s) { explain = s; } ///< Constructor
}; };
/// \brief An exception describing a parsing error in a command line
///
/// Thrown when attempting to parse a command line. Options are missing or are in
/// the wrong form etc.
struct IfaceParseError : public IfaceError { struct IfaceParseError : public IfaceError {
IfaceParseError(const string &s) : IfaceError(s) {} IfaceParseError(const string &s) : IfaceError(s) {} ///< Constructor
}; };
/// \brief An exception throw during the execution of a command
///
/// Processing of a specific command has started but has reached an error state
struct IfaceExecutionError : public IfaceError { struct IfaceExecutionError : public IfaceError {
IfaceExecutionError(const string &s) : IfaceError(s) {} IfaceExecutionError(const string &s) : IfaceError(s) {} ///< Constructor
}; };
class IfaceStatus; // Forward declaration class IfaceStatus; // Forward declaration
class IfaceData { // Data specialized for a particular command /// \brief Data specialized for a particular command module
///
/// IfaceCommands can have specialized data that is shared with other commands in
/// the same module. This is the root object for all such data.
class IfaceData {
public: public:
virtual ~IfaceData(void) {} virtual ~IfaceData(void) {} ///< Destructor
}; };
/// \brief A command that can be executed from the command line
///
/// The command has data associated with it (via setData()) and is executed
/// via the execute() method. The command can get additional parameters from
/// the command line by reading the input stream passed to it.
/// The command is associated with a specific sequence of words (tokens)
/// that should appear at the start of the command line.
class IfaceCommand { class IfaceCommand {
vector<string> com; // The command vector<string> com; ///< The token sequence associated with the command
public: public:
virtual ~IfaceCommand(void) {} virtual ~IfaceCommand(void) {} ///< Destructor
/// \brief Associate a specific data object with this command.
///
/// \param root is the interface object this command is registered with
/// \param data is the data object the command should use
virtual void setData(IfaceStatus *root,IfaceData *data)=0; virtual void setData(IfaceStatus *root,IfaceData *data)=0;
/// Execute this command. Additional state can be read from the given command line stream.
/// Otherwise, the command gets its data from its registered IfaceData object
/// \param s is the input stream from the command line
virtual void execute(istream &s)=0; virtual void execute(istream &s)=0;
/// \brief Get the formal module name to which this command belongs
///
/// Commands in the same module share data through their registered IfaceData object
/// \return the formal module name
virtual string getModule(void) const=0; virtual string getModule(void) const=0;
/// \brief Create a specialized data object for \b this command (and its module)
///
/// This method is only called once per module
/// \return the newly created data object for the module
virtual IfaceData *createData(void)=0; virtual IfaceData *createData(void)=0;
/// \brief Add a token to the command line string associated with this command
///
/// \param temp is the new token to add
void addWord(const string &temp) { com.push_back(temp); } void addWord(const string &temp) { com.push_back(temp); }
void removeWord(void) { com.pop_back(); }
const string &getCommandWord(int4 i) const { return com[i]; } void removeWord(void) { com.pop_back(); } ///< Remove the last token from the associated command line string
void addWords(const vector<string> &wordlist); const string &getCommandWord(int4 i) const { return com[i]; } ///< Get the i-th command token
int4 numWords(void) const { return com.size(); } void addWords(const vector<string> &wordlist); ///< Add words to the associated command line string
void commandString(string &res) const; int4 numWords(void) const { return com.size(); } ///< Return the number of tokens in the command line string
int4 compare(const IfaceCommand &op2) const; void commandString(string &res) const; ///< Get the complete command line string
int4 compare(const IfaceCommand &op2) const; ///< Order two commands by their command line strings
}; };
/// \brief A dummy command used during parsing
class IfaceCommandDummy : public IfaceCommand { class IfaceCommandDummy : public IfaceCommand {
public: public:
virtual void setData(IfaceStatus *root,IfaceData *data) {} virtual void setData(IfaceStatus *root,IfaceData *data) {}
@ -115,74 +144,113 @@ public:
virtual IfaceData *createData(void) { return (IfaceData *)0; } virtual IfaceData *createData(void) { return (IfaceData *)0; }
}; };
/// \brief Compare to commands as pointers
///
/// \param a is a pointer to the first command
/// \param b is a pointer to the second command
/// \return \b true if the first pointer is ordered before the second
inline bool compare_ifacecommand(const IfaceCommand *a,const IfaceCommand *b) { inline bool compare_ifacecommand(const IfaceCommand *a,const IfaceCommand *b) {
return (0>a->compare(*b)); return (0>a->compare(*b));
} }
/// \brief Groups of console commands that are \e discovered by the loader
///
/// Any IfaceCommand that is registered with a grouping derived from this class
/// is automatically made available to any IfaceStatus object just by calling
/// the static registerAllCommands()
class IfaceCapability : public CapabilityPoint { class IfaceCapability : public CapabilityPoint {
static vector<IfaceCapability *> thelist; static vector<IfaceCapability *> thelist; ///< The global list of discovered command groupings
protected: protected:
string name; // Identifying name for the capability string name; ///< Identifying name for the capability
public: public:
const string &getName(void) const { return name; } const string &getName(void) const { return name; } ///< Get the name of the capability
virtual void initialize(void); virtual void initialize(void);
virtual void registerCommands(IfaceStatus *status)=0; virtual void registerCommands(IfaceStatus *status)=0; ///< Register commands for \b this grouping
static void registerAllCommands(IfaceStatus *status); static void registerAllCommands(IfaceStatus *status); ///< Register all discovered commands with the interface
}; };
/// \brief A generic console mode interface and command executor
///
/// Input is provided one command line at a time by providing calling readLine().
/// Output goes to a provided ostream, \e optr. Output to a separate bulk stream
/// can be enabled by setting \e fileoptr.
///
/// A derived IfaceCommand is attached to a command string via registerCom()
/// i.e.
/// stat.registerCom(new IfcQuit(),"quit");
/// stat.registerCom(new IfcOpenfileAppend(),"openfile","append");
/// stat.mainloop();
/// Command line processing is started with mainloop(), which prints a command prompt,
/// allows command line editing, including command completion and history, and executes
/// the corresponding IfaceComman::execute() callback.
/// Command words only have to match enough to disambiguate it from other commands.
/// A Custom history size and command prompt can be passed to the constructor.
/// Applications should inherit from base class IfaceStatus in order to
/// - Override the readLine() method
/// - Override pushScript() and popScript() to allow command scripts
/// - Get custom data into IfaceCommand callbacks
class IfaceStatus { class IfaceStatus {
vector<istream *> inputstack; vector<string> promptstack; ///< Stack of command prompts corresponding to script nesting level
vector<string> promptstack; vector<uint4> flagstack; ///< Stack of flag state corresponding to script nesting level
vector<uint4> flagstack; string prompt; ///< The current command prompt
string prompt; int4 maxhistory; ///< Maximum number of command lines to store in history
int4 maxhistory; int4 curhistory; ///< Most recent history
int4 curhistory; // most recent history vector<string> history; ///< History of commands executed through this interface
vector<string> history; bool sorted; ///< Set to \b true if commands are sorted
bool sorted; // Are commands sorted bool errorisdone; ///< Set to \b true if any error terminates the process
bool inerror; // -true- if last command did not succeed void restrictCom(vector<IfaceCommand *>::const_iterator &first,
bool errorisdone; // -true- if any error terminates the process vector<IfaceCommand *>::const_iterator &last,vector<string> &input);
void restrict(vector<IfaceCommand *>::const_iterator &first,vector<IfaceCommand *>::const_iterator &last,vector<string> &input);
virtual void readLine(string &line) { getline(*sptr,line,'\n'); } /// \brief Read the next command line
void saveHistory(const string &line); ///
/// \param line is filled in with the next command to execute
virtual void readLine(string &line)=0;
void saveHistory(const string &line); ///< Store the given command line into \e history
protected: protected:
istream *sptr; // Where to get input bool inerror; ///< Set to \b true if last command did not succeed
vector<IfaceCommand *> comlist; // List of commands vector<IfaceCommand *> comlist; ///< List of registered commands
map<string,IfaceData *> datamap; // Data associated with particular modules map<string,IfaceData *> datamap; ///< Data associated with particular modules
int4 expandCom(vector<string> &expand,istream &s, int4 expandCom(vector<string> &expand,istream &s,
vector<IfaceCommand *>::const_iterator &first, vector<IfaceCommand *>::const_iterator &first,
vector<IfaceCommand *>::const_iterator &last); vector<IfaceCommand *>::const_iterator &last);
public: public:
bool done; bool done; ///< Set to \b true (by a command) to indicate processing is finished
ostream *optr; // Where to put command line output ostream *optr; ///< Where to put command line output
ostream *fileoptr; // Where to put bulk output ostream *fileoptr; ///< Where to put bulk output
IfaceStatus(const string &prmpt,istream &is,ostream &os,int4 mxhist=10); IfaceStatus(const string &prmpt,ostream &os,int4 mxhist=10); ///< Constructor
virtual ~IfaceStatus(void); virtual ~IfaceStatus(void); ///< Destructor
void setErrorIsDone(bool val) { errorisdone = val; } void setErrorIsDone(bool val) { errorisdone = val; } ///< Set if processing should terminate on an error
void pushScript(const string &filename,const string &newprompt); virtual void pushScript(const string &filename,const string &newprompt);
void popScript(void); virtual void popScript(void);
void reset(void); void reset(void); ///< Pop any existing script streams and return to processing from the base stream
int4 getNumInputStreamSize(void) const { return inputstack.size(); } int4 getNumInputStreamSize(void) const { return promptstack.size(); } ///< Get depth of script nesting
void writePrompt(void) { *optr << prompt; } void writePrompt(void) { *optr << prompt; } ///< Write the current command prompt to the current output stream
void registerCom(IfaceCommand *fptr, const char *nm1, void registerCom(IfaceCommand *fptr, const char *nm1,
const char *nm2 = (const char *)0, const char *nm2 = (const char *)0,
const char *nm3 = (const char *)0, const char *nm3 = (const char *)0,
const char *nm4 = (const char *)0, const char *nm4 = (const char *)0,
const char *nm5 = (const char *)0); const char *nm5 = (const char *)0);
IfaceData *getData(const string &nm) const; IfaceData *getData(const string &nm) const; ///< Get data associated with a IfaceCommand module
bool runCommand(void); bool runCommand(void); ///< Run the next command
void getHistory(string &line,int4 i) const; void getHistory(string &line,int4 i) const; ///< Get the i-th command line from history
int4 getHistorySize(void) const { return history.size(); } int4 getHistorySize(void) const { return history.size(); } ///< Get the number of command lines in history
bool isStreamFinished(void) const { if (done||inerror) return true; return sptr->eof(); } virtual bool isStreamFinished(void) const=0; ///< Return \b true if the current stream is finished
bool isInError(void) const { return inerror; } bool isInError(void) const { return inerror; } ///< Return \b true if the last command failed
void evaluateError(void); void evaluateError(void); ///< Adjust which stream to process based on last error
static void wordsToString(string &res,const vector<string> &list); static void wordsToString(string &res,const vector<string> &list); ///< Concatenate tokens
}; };
/// \brief A root class for a basic set of commands
///
/// Commands derived from this class are in the "base" module.
/// They are useful as part of any interface
class IfaceBaseCommand : public IfaceCommand { class IfaceBaseCommand : public IfaceCommand {
protected: protected:
IfaceStatus *status; IfaceStatus *status; ///< The interface owning this command instance
public: public:
virtual void setData(IfaceStatus *root,IfaceData *data) { status = root; } virtual void setData(IfaceStatus *root,IfaceData *data) { status = root; }
virtual string getModule(void) const { return "base"; } virtual string getModule(void) const { return "base"; }

View file

@ -500,7 +500,7 @@ class JumpTable {
/// \brief An address table index and its corresponding out-edge /// \brief An address table index and its corresponding out-edge
struct IndexPair { struct IndexPair {
int4 blockPosition; ///< Out-edge index for the basic-block int4 blockPosition; ///< Out-edge index for the basic-block
int4 addressIndex; /// Index of address targetting the basic-block int4 addressIndex; ///< Index of address targeting the basic-block
IndexPair(int4 pos,int4 index) { blockPosition = pos; addressIndex = index; } ///< Constructor IndexPair(int4 pos,int4 index) { blockPosition = pos; addressIndex = index; } ///< Constructor
bool operator<(const IndexPair &op2) const; ///< Compare by position then by index bool operator<(const IndexPair &op2) const; ///< Compare by position then by index
static bool compareByPosition(const IndexPair &op1,const IndexPair &op2); ///< Compare just by position static bool compareByPosition(const IndexPair &op1,const IndexPair &op2); ///< Compare just by position

View file

@ -838,6 +838,10 @@ string OptionAliasBlock::apply(Architecture *glb,const string &p1,const string &
return "Alias block level set to " + p1; return "Alias block level set to " + p1;
} }
/// \class OptionMaxInstruction
/// \brief Maximum number of instructions that can be processed in a single function
///
/// The first parameter is an integer specifying the maximum.
string OptionMaxInstruction::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const string OptionMaxInstruction::apply(Architecture *glb,const string &p1,const string &p2,const string &p3) const
{ {

View file

@ -249,7 +249,7 @@ void Override::printRaw(ostream &s,Architecture *glb) const
s << "dead code delay on " << spc->getName() << " set to " << dec << deadcodedelay[i] << endl; s << "dead code delay on " << spc->getName() << " set to " << dec << deadcodedelay[i] << endl;
} }
for(iter=forcegoto.begin();iter!=forcegoto.end();++iter) for(iter=indirectover.begin();iter!=indirectover.end();++iter)
s << "override indirect at " << (*iter).first << " to call directly to " << (*iter).second << endl; s << "override indirect at " << (*iter).first << " to call directly to " << (*iter).second << endl;
map<Address,FuncProto *>::const_iterator fiter; map<Address,FuncProto *>::const_iterator fiter;

View file

@ -727,7 +727,7 @@ class EmitPrettyPrint : public EmitXml {
void print(const TokenSplit &tok); ///< Output the given token to the low-level emitter void print(const TokenSplit &tok); ///< Output the given token to the low-level emitter
void advanceleft(void); ///< Emit tokens that have been fully committed void advanceleft(void); ///< Emit tokens that have been fully committed
void scan(void); ///< Process a new token void scan(void); ///< Process a new token
void resetDefaultsPrettyPrint(void) { setMaxLineSize(100); } void resetDefaultsPrettyPrint(void) { setMaxLineSize(100); } ///< Reset the defaults
public: public:
EmitPrettyPrint(void); ///< Construct with an initial maximum line size EmitPrettyPrint(void); ///< Construct with an initial maximum line size
virtual ~EmitPrettyPrint(void); virtual ~EmitPrettyPrint(void);

View file

@ -199,9 +199,9 @@ void PrintC::pushSymbolScope(const Symbol *symbol)
} }
} }
/// Emit the elements of the given function's namespace path that distinguish it within /// Emit the elements of the given symbol's namespace path that distinguish it within
/// the current scope. /// the current scope.
/// \param fd is the given function /// \param symbol is the given Symbol
void PrintC::emitSymbolScope(const Symbol *symbol) void PrintC::emitSymbolScope(const Symbol *symbol)
{ {

View file

@ -9085,6 +9085,11 @@ int4 RulePiecePathology::applyOp(PcodeOp *op,Funcdata &data)
return tracePathologyForward(op, data); return tracePathologyForward(op, data);
} }
/// \class RuleXorSwap
/// \brief Simplify limited chains of XOR operations
///
/// `V = (a ^ b) ^ a => V = b`
/// `V = a ^ (b ^ a) => V = b`
void RuleXorSwap::getOpList(vector<uint4> &oplist) const void RuleXorSwap::getOpList(vector<uint4> &oplist) const
{ {

View file

@ -32,10 +32,13 @@ PcodeCacher::~PcodeCacher(void)
delete [] poolstart; delete [] poolstart;
} }
/// Expand the VarnodeData pool so that \e size more elements fit, and return
/// a pointer to first available element.
/// \param size is the number of elements to expand the pool by
/// \return the first available VarnodeData
VarnodeData *PcodeCacher::expandPool(uint4 size) VarnodeData *PcodeCacher::expandPool(uint4 size)
{ // Expand the pool so that -size- more elements fit {
// Return pointer to first available element
uint4 curmax = endpool - poolstart; uint4 curmax = endpool - poolstart;
uint4 cursize = curpool - poolstart; uint4 cursize = curpool - poolstart;
if (cursize + size <= curmax) if (cursize + size <= curmax)
@ -75,18 +78,26 @@ VarnodeData *PcodeCacher::expandPool(uint4 size)
return newpool + cursize; return newpool + cursize;
} }
/// Store off a reference to the Varnode and the absolute index of the next
/// instruction. The Varnode must be an operand of the current instruction.
/// \param ptr is the Varnode reference
void PcodeCacher::addLabelRef(VarnodeData *ptr) void PcodeCacher::addLabelRef(VarnodeData *ptr)
{ // Store off a reference to a label and the next instruction {
// address
label_refs.emplace_back(); label_refs.emplace_back();
label_refs.back().dataptr = ptr; label_refs.back().dataptr = ptr;
label_refs.back().calling_index = issued.size(); label_refs.back().calling_index = issued.size();
} }
/// The label has an id that is referred to by Varnodes holding
/// intra-instruction branch targets, prior to converting
/// them to a \e relative \e branch offset. The label is associated with
/// the absolute index of the next PcodeData object to be issued,
/// facilitating this conversion.
/// \param id is the given id of the label
void PcodeCacher::addLabel(uint4 id) void PcodeCacher::addLabel(uint4 id)
{ // Attach a label to the address of the next instruction {
while(labels.size() <= id) while(labels.size() <= id)
labels.push_back(0xbadbeef); labels.push_back(0xbadbeef);
labels[ id ] = issued.size(); labels[ id ] = issued.size();
@ -101,11 +112,12 @@ void PcodeCacher::clear(void)
labels.clear(); labels.clear();
} }
/// Assuming all the PcodeData has been generated for an
/// instruction, go resolve any relative offsets and back
/// patch their value(s) into the PcodeData
void PcodeCacher::resolveRelatives(void) void PcodeCacher::resolveRelatives(void)
{ // Assuming all the PcodeData has been generated for an {
// instruction, go resolve any relative offsets and back
// patch their value(s) into the PcodeData
list<RelativeRecord>::const_iterator iter; list<RelativeRecord>::const_iterator iter;
for(iter=label_refs.begin();iter!=label_refs.end();++iter) { for(iter=label_refs.begin();iter!=label_refs.end();++iter) {
VarnodeData *ptr = (*iter).dataptr; VarnodeData *ptr = (*iter).dataptr;
@ -119,18 +131,25 @@ void PcodeCacher::resolveRelatives(void)
} }
} }
/// Each p-code operation is presented to the emitter via its dump() method.
/// \param addr is the Address associated with the p-code operation
/// \param emt is the emitter
void PcodeCacher::emit(const Address &addr,PcodeEmit *emt) const void PcodeCacher::emit(const Address &addr,PcodeEmit *emt) const
{ // Emit any cached pcode {
vector<PcodeData>::const_iterator iter; vector<PcodeData>::const_iterator iter;
for(iter=issued.begin();iter!=issued.end();++iter) for(iter=issued.begin();iter!=issued.end();++iter)
emt->dump(addr,(*iter).opc,(*iter).outvar,(*iter).invar,(*iter).isize); emt->dump(addr,(*iter).opc,(*iter).outvar,(*iter).invar,(*iter).isize);
} }
/// \brief Generate a concrete VarnodeData object from the given template (VarnodeTpl)
///
/// \param vntpl is the template to reference
/// \param vn is the object to fill in with concrete values
void SleighBuilder::generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn) void SleighBuilder::generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn)
{ // Generate a concrete varnode -vn- from the template -vntpl- {
vn.space = vntpl->getSpace().fixSpace(*walker); vn.space = vntpl->getSpace().fixSpace(*walker);
vn.size = vntpl->getSize().fix(*walker); vn.size = vntpl->getSize().fix(*walker);
if (vn.space == const_space) if (vn.space == const_space)
@ -143,9 +162,18 @@ void SleighBuilder::generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn)
vn.offset = vn.space->wrapOffset(vntpl->getOffset().fix(*walker)); vn.offset = vn.space->wrapOffset(vntpl->getOffset().fix(*walker));
} }
/// \brief Generate a pointer VarnodeData from a dynamic template (VarnodeTpl)
///
/// The symbol represents a value referenced through a dynamic pointer.
/// This method generates the varnode representing the pointer itself and also
/// returns the address space in anticipation of generating the LOAD or STORE
/// that actually manipulates the value.
/// \param vntpl is the dynamic template to reference
/// \param vn is the object to fill with concrete values
/// \return the address space being pointed to
AddrSpace *SleighBuilder::generatePointer(const VarnodeTpl *vntpl,VarnodeData &vn) AddrSpace *SleighBuilder::generatePointer(const VarnodeTpl *vntpl,VarnodeData &vn)
{ // Generate the pointer varnode -vn- from a dynamic template -vntpl- {
const FixedHandle &hand(walker->getFixedHandle(vntpl->getOffset().getHandleIndex())); const FixedHandle &hand(walker->getFixedHandle(vntpl->getOffset().getHandleIndex()));
vn.space = hand.offset_space; vn.space = hand.offset_space;
vn.size = hand.offset_size; vn.size = hand.offset_size;
@ -218,9 +246,17 @@ void SleighBuilder::dump(OpTpl *op)
} }
} }
/// \brief Build a named p-code section of a constructor that contains only implied BUILD directives
///
/// If a named section of a constructor is empty, we still need to walk
/// through any subtables that might contain p-code in their named sections.
/// This method treats each subtable operand as an implied \e build directive,
/// in the otherwise empty section.
/// \param ct is the matching currently Constructor being built
/// \param secnum is the particular \e named section number to build
void SleighBuilder::buildEmpty(Constructor *ct,int4 secnum) void SleighBuilder::buildEmpty(Constructor *ct,int4 secnum)
{ // Build a named p-code section of a constructor that contains only implied BUILD directives {
int4 numops = ct->getNumOperands(); int4 numops = ct->getNumOperands();
for(int4 i=0;i<numops;++i) { for(int4 i=0;i<numops;++i) {
@ -238,12 +274,23 @@ void SleighBuilder::buildEmpty(Constructor *ct,int4 secnum)
} }
} }
/// Bits used to make temporary registers unique across multiple instructions
/// are generated based on the given address.
/// \param addr is the given Address
void SleighBuilder::setUniqueOffset(const Address &addr) void SleighBuilder::setUniqueOffset(const Address &addr)
{ {
uniqueoffset = (addr.getOffset() & uniquemask)<<4; uniqueoffset = (addr.getOffset() & uniquemask)<<4;
} }
/// \brief Constructor
///
/// \param w is the parsed instruction
/// \param dcache is a cache of nearby instruction parses
/// \param pc will hold the PcodeData and VarnodeData objects produced by \b this builder
/// \param cspc is the constant address space
/// \param uspc is the unique address space
/// \param umask is the mask to use to find unique bits within an Address
SleighBuilder::SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCacher *pc,AddrSpace *cspc, SleighBuilder::SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCacher *pc,AddrSpace *cspc,
AddrSpace *uspc,uint4 umask) AddrSpace *uspc,uint4 umask)
: PcodeBuilder(0) : PcodeBuilder(0)
@ -259,7 +306,8 @@ SleighBuilder::SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCache
void SleighBuilder::appendBuild(OpTpl *bld,int4 secnum) void SleighBuilder::appendBuild(OpTpl *bld,int4 secnum)
{ // Append pcode for a particular build statement {
// Append p-code for a particular build statement
int4 index = bld->getIn(0)->getOffset().getReal(); // Recover operand index from build statement int4 index = bld->getIn(0)->getOffset().getReal(); // Recover operand index from build statement
// Check if operand is a subtable // Check if operand is a subtable
SubtableSymbol *sym = (SubtableSymbol *)walker->getConstructor()->getOperand(index)->getDefiningSymbol(); SubtableSymbol *sym = (SubtableSymbol *)walker->getConstructor()->getOperand(index)->getDefiningSymbol();
@ -283,8 +331,9 @@ void SleighBuilder::appendBuild(OpTpl *bld,int4 secnum)
void SleighBuilder::delaySlot(OpTpl *op) void SleighBuilder::delaySlot(OpTpl *op)
{ // Append pcode for an entire instruction (delay slot) {
// in the middle of the current instruction // Append pcode for an entire instruction (delay slot)
// in the middle of the current instruction
ParserWalker *tmp = walker; ParserWalker *tmp = walker;
uintb olduniqueoffset = uniqueoffset; uintb olduniqueoffset = uniqueoffset;
@ -319,7 +368,8 @@ void SleighBuilder::setLabel(OpTpl *op)
void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum) void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum)
{ // Weave in the p-code section from an instruction at another address {
// Weave in the p-code section from an instruction at another address
// bld-param(0) contains the address of the instruction // bld-param(0) contains the address of the instruction
// bld-param(1) contains the section number // bld-param(1) contains the section number
if (secnum>=0) if (secnum>=0)
@ -352,6 +402,8 @@ void SleighBuilder::appendCrossBuild(OpTpl *bld,int4 secnum)
uniqueoffset = olduniqueoffset; uniqueoffset = olduniqueoffset;
} }
/// \param min is the minimum number of allocations before a reuse is expected
/// \param hashsize is the number of elements in the hash-table
void DisassemblyCache::initialize(int4 min,int4 hashsize) void DisassemblyCache::initialize(int4 min,int4 hashsize)
{ {
@ -382,6 +434,10 @@ void DisassemblyCache::free(void)
delete [] hashtable; delete [] hashtable;
} }
/// \param ccache is the ContextCache front-end shared across all the parser contexts
/// \param cspace is the constant address space used for minting constant Varnodes
/// \param cachesize is the number of distinct ParserContext objects in this cache
/// \param windowsize is the size of the ParserContext hash-table
DisassemblyCache::DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize) DisassemblyCache::DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize)
{ {
@ -390,13 +446,17 @@ DisassemblyCache::DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 c
initialize(cachesize,windowsize); // Set default settings for the cache initialize(cachesize,windowsize); // Set default settings for the cache
} }
/// Return a (possibly cached) ParserContext that is associated with \e addr
/// If n different calls to this interface are made with n different Addresses, if
/// - n <= minimumreuse AND
/// - all the addresses are within the windowsize (=mask+1)
///
/// then the cacher guarantees that you get all different ParserContext objects
/// \param addr is the Address to disassemble at
/// \return the ParserContext associated with the address
ParserContext *DisassemblyCache::getParserContext(const Address &addr) ParserContext *DisassemblyCache::getParserContext(const Address &addr)
{ // Return a (possibly cached) ParserContext that is associated with -addr- {
// If n different calls to this interface are made with n different Addresses, if
// n <= minimumreuse AND
// all the addresses are within the windowsize (=mask+1)
// then the cacher guarantees that you get all different ParserContext objects
int4 hashindex = ((int4) addr.getOffset()) & mask; int4 hashindex = ((int4) addr.getOffset()) & mask;
ParserContext *res = hashtable[ hashindex ]; ParserContext *res = hashtable[ hashindex ];
if (res->getAddr() == addr) if (res->getAddr() == addr)
@ -411,6 +471,8 @@ ParserContext *DisassemblyCache::getParserContext(const Address &addr)
return res; return res;
} }
/// \param ld is the LoadImage to draw program bytes from
/// \param c_db is the context database
Sleigh::Sleigh(LoadImage *ld,ContextDatabase *c_db) Sleigh::Sleigh(LoadImage *ld,ContextDatabase *c_db)
: SleighBase() : SleighBase()
@ -435,10 +497,13 @@ Sleigh::~Sleigh(void)
clearForDelete(); clearForDelete();
} }
/// Completely clear everything except the base and reconstruct
/// with a new LoadImage and ContextDatabase
/// \param ld is the new LoadImage
/// \param c_db is the new ContextDatabase
void Sleigh::reset(LoadImage *ld,ContextDatabase *c_db) void Sleigh::reset(LoadImage *ld,ContextDatabase *c_db)
{ // Completely clear everything except the base and reconstruct {
// with a new loader and context
clearForDelete(); clearForDelete();
pcode_cache.clear(); pcode_cache.clear();
loader = ld; loader = ld;
@ -447,6 +512,8 @@ void Sleigh::reset(LoadImage *ld,ContextDatabase *c_db)
discache = (DisassemblyCache *)0; discache = (DisassemblyCache *)0;
} }
/// The .sla file from the document store is loaded and cache objects are prepared
/// \param store is the document store containing the main \<sleigh> tag.
void Sleigh::initialize(DocumentStorage &store) void Sleigh::initialize(DocumentStorage &store)
{ {
@ -467,10 +534,18 @@ void Sleigh::initialize(DocumentStorage &store)
discache = new DisassemblyCache(cache,getConstantSpace(),parser_cachesize,parser_windowsize); discache = new DisassemblyCache(cache,getConstantSpace(),parser_cachesize,parser_windowsize);
} }
/// \brief Obtain a parse tree for the instruction at the given address
///
/// The tree may be cached from a previous access. If the address
/// has not been parsed, disassembly is performed, and a new parse tree
/// is prepared. Depending on the desired \e state, the parse tree
/// can be prepared either for disassembly or for p-code generation.
/// \param addr is the given address of the instruction
/// \param state is the desired parse state.
/// \return the parse tree object (ParseContext)
ParserContext *Sleigh::obtainContext(const Address &addr,int4 state) const ParserContext *Sleigh::obtainContext(const Address &addr,int4 state) const
{ // Obtain a ParserContext for the instruction at the given -addr-. This may be cached. {
// Make sure parsing has proceeded to at least the given -state.
ParserContext *pos = discache->getParserContext(addr); ParserContext *pos = discache->getParserContext(addr);
int4 curstate = pos->getParserState(); int4 curstate = pos->getParserState();
if (curstate >= state) if (curstate >= state)
@ -485,10 +560,11 @@ ParserContext *Sleigh::obtainContext(const Address &addr,int4 state) const
return pos; return pos;
} }
/// Resolve \e all the constructors involved in the instruction at the indicated address
/// \param pos is the parse object that will hold the resulting tree
void Sleigh::resolve(ParserContext &pos) const void Sleigh::resolve(ParserContext &pos) const
{ // Resolve ALL the constructors involved in the {
// instruction at this address
loader->loadFill(pos.getBuffer(),16,pos.getAddr()); loader->loadFill(pos.getBuffer(),16,pos.getAddr());
ParserWalkerChange walker(&pos); ParserWalkerChange walker(&pos);
pos.deallocateState(walker); // Clear the previous resolve and initialize the walker pos.deallocateState(walker); // Clear the previous resolve and initialize the walker
@ -538,9 +614,12 @@ void Sleigh::resolve(ParserContext &pos) const
pos.setParserState(ParserContext::disassembly); pos.setParserState(ParserContext::disassembly);
} }
/// Resolve handle templates for the given parse tree, assuming Constructors
/// are already resolved.
/// \param pos is the given parse tree
void Sleigh::resolveHandles(ParserContext &pos) const void Sleigh::resolveHandles(ParserContext &pos) const
{ // Resolve handles (assuming Constructors already resolved) {
TripleSymbol *triple; TripleSymbol *triple;
Constructor *ct; Constructor *ct;
int4 oper,numoper; int4 oper,numoper;
@ -671,7 +750,7 @@ int4 Sleigh::oneInstruction(PcodeEmit &emit,const Address &baseaddr) const
void Sleigh::registerContext(const string &name,int4 sbit,int4 ebit) void Sleigh::registerContext(const string &name,int4 sbit,int4 ebit)
{ // Inform translator of existence of context variable {
context_db->registerVariable(name,sbit,ebit); context_db->registerVariable(name,sbit,ebit);
} }

View file

@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/// \file sleigh.hh
/// \brief Classes and utilities for the main SLEIGH engine
#ifndef __SLEIGH__ #ifndef __SLEIGH__
#define __SLEIGH__ #define __SLEIGH__
@ -20,29 +23,52 @@
class LoadImage; class LoadImage;
/// \brief Class for describing a relative p-code branch destination
///
/// An intra-instruction p-code branch takes a \e relative operand.
/// The actual value produced during p-code generation is calculated at
/// the last second using \b this. It stores the index of the BRANCH
/// instruction and a reference to its destination operand. This initially
/// holds a reference to a destination \e label symbol, but is later updated
/// with the final relative value.
struct RelativeRecord { struct RelativeRecord {
VarnodeData *dataptr; // Record containing relative offset VarnodeData *dataptr; ///< Varnode indicating relative offset
uintb calling_index; // Index of instruction containing relative offset uintb calling_index; ///< Index of instruction containing relative offset
}; };
struct PcodeData { // Data for building one pcode instruction /// \brief Data for building one p-code instruction
OpCode opc; ///
VarnodeData *outvar; // Points to outvar is there is an output /// Raw data used by the emitter to produce a single PcodeOp
VarnodeData *invar; // Inputs struct PcodeData {
int4 isize; // Number of inputs OpCode opc; ///< The op code
VarnodeData *outvar; ///< Output Varnode data (or null)
VarnodeData *invar; ///< Array of input Varnode data
int4 isize; ///< Number of input Varnodes
}; };
class PcodeCacher { // Cached chunk of pcode, prior to emitting /// \brief Class for caching a chunk of p-code, prior to emitting
VarnodeData *poolstart; ///
VarnodeData *curpool; /// The engine accumulates PcodeData and VarnodeData objects for
VarnodeData *endpool; /// a single instruction. Once the full instruction is constructed,
vector<PcodeData> issued; /// the objects are passed to the emitter (PcodeEmit) via the emit() method.
list<RelativeRecord> label_refs; // References to labels /// The class acts as a pool of memory for PcodeData and VarnodeData objects
vector<uintb> labels; // Locations of labels /// that can be reused repeatedly to emit multiple instructions.
VarnodeData *expandPool(uint4 size); class PcodeCacher {
VarnodeData *poolstart; ///< Start of the pool of VarnodeData objects
VarnodeData *curpool; ///< First unused VarnodeData
VarnodeData *endpool; ///< End of the pool of VarnodeData objects
vector<PcodeData> issued; ///< P-code ops issued for the current instruction
list<RelativeRecord> label_refs; ///< References to labels
vector<uintb> labels; ///< Locations of labels
VarnodeData *expandPool(uint4 size); ///< Expand the memory pool
public: public:
PcodeCacher(void); PcodeCacher(void); ///< Constructor
~PcodeCacher(void); ~PcodeCacher(void); ///< Destructor
/// \brief Allocate data objects for a new set of Varnodes
///
/// \param size is the number of objects to allocate
/// \return a pointer to the array of available VarnodeData objects
VarnodeData *allocateVarnodes(uint4 size) { VarnodeData *allocateVarnodes(uint4 size) {
VarnodeData *newptr = curpool + size; VarnodeData *newptr = curpool + size;
if (newptr <= endpool) { if (newptr <= endpool) {
@ -52,6 +78,10 @@ public:
} }
return expandPool(size); return expandPool(size);
} }
/// \brief Allocate a data object for a new p-code operation
///
/// \return the new PcodeData object
PcodeData *allocateInstruction(void) { PcodeData *allocateInstruction(void) {
issued.emplace_back(); issued.emplace_back();
PcodeData *res = &issued.back(); PcodeData *res = &issued.back();
@ -59,41 +89,54 @@ public:
res->invar = (VarnodeData *)0; res->invar = (VarnodeData *)0;
return res; return res;
} }
void addLabelRef(VarnodeData *ptr); void addLabelRef(VarnodeData *ptr); ///< Denote a Varnode holding a \e relative \e branch offset
void addLabel(uint4 id); void addLabel(uint4 id); ///< Attach a label to the \e next p-code instruction
void clear(void); void clear(void); ///< Reset the cache so that all objects are unallocated
void resolveRelatives(void); void resolveRelatives(void); ///< Rewrite branch target Varnodes as \e relative offsets
void emit(const Address &addr,PcodeEmit *emt) const; void emit(const Address &addr,PcodeEmit *emt) const; ///< Pass the cached p-code data to the emitter
}; };
/// \brief A container for disassembly context used by the SLEIGH engine
///
/// This acts as a factor for the ParserContext objects which are used to disassemble
/// a single instruction. These all share a ContextCache which is a front end for
/// accessing the ContextDatabase and resolving context variables from the SLEIGH spec.
/// ParserContext objects are stored in a hash-table keyed by the address of the instruction.
class DisassemblyCache { class DisassemblyCache {
ContextCache *contextcache; ContextCache *contextcache; ///< Cached values from the ContextDatabase
AddrSpace *constspace; AddrSpace *constspace; ///< The constant address space
int4 minimumreuse; // Can call getParserContext this many times, before a ParserContext is reused int4 minimumreuse; ///< Can call getParserContext this many times, before a ParserContext is reused
uint4 mask; // Size of the hashtable in form 2^n-1 uint4 mask; ///< Size of the hashtable in form 2^n-1
ParserContext **list; // (circular) array of currently cached ParserContext objects ParserContext **list; ///< (circular) array of currently cached ParserContext objects
int4 nextfree; // Current end/beginning of circular list int4 nextfree; ///< Current end/beginning of circular list
ParserContext **hashtable; // Hashtable for looking up ParserContext via Address ParserContext **hashtable; ///< Hashtable for looking up ParserContext via Address
void initialize(int4 min,int4 hashsize); void initialize(int4 min,int4 hashsize); ///< Initialize the hash-table of ParserContexts
void free(void); void free(void); ///< Free the hash-table of ParserContexts
public: public:
DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize); DisassemblyCache(ContextCache *ccache,AddrSpace *cspace,int4 cachesize,int4 windowsize); ///< Constructor
~DisassemblyCache(void) { free(); } ~DisassemblyCache(void) { free(); } ///< Destructor
ParserContext *getParserContext(const Address &addr); ParserContext *getParserContext(const Address &addr); ///< Get the parser for a particular Address
}; };
/// \brief Build p-code from a pre-parsed instruction
///
/// Through the build() method, \b this walks the parse tree and prepares data
/// for final emission as p-code. (The final emitting is done separately through the
/// PcodeCacher.emit() method). Generally, only p-code for one instruction is prepared.
/// But, through the \b delay-slot mechanism, build() may recursively visit
/// additional instructions.
class SleighBuilder : public PcodeBuilder { class SleighBuilder : public PcodeBuilder {
virtual void dump( OpTpl *op ); virtual void dump( OpTpl *op );
AddrSpace *const_space; AddrSpace *const_space; ///< The constant address space
AddrSpace *uniq_space; AddrSpace *uniq_space; ///< The unique address space
uintb uniquemask; uintb uniquemask; ///< Mask of address bits to use to uniquify temporary registers
uintb uniqueoffset; uintb uniqueoffset; ///< Uniquifier bits for \b this instruction
DisassemblyCache *discache; DisassemblyCache *discache; ///< Cache of disassembled instructions
PcodeCacher *cache; PcodeCacher *cache; ///< Cache accumulating p-code data for the instruction
void buildEmpty(Constructor *ct,int4 secnum); void buildEmpty(Constructor *ct,int4 secnum);
void generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn); void generateLocation(const VarnodeTpl *vntpl,VarnodeData &vn);
AddrSpace *generatePointer(const VarnodeTpl *vntpl,VarnodeData &vn); AddrSpace *generatePointer(const VarnodeTpl *vntpl,VarnodeData &vn);
void setUniqueOffset(const Address &addr); void setUniqueOffset(const Address &addr); ///< Set uniquifying bits for the current instruction
public: public:
SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCacher *pc,AddrSpace *cspc,AddrSpace *uspc,uint4 umask); SleighBuilder(ParserWalker *w,DisassemblyCache *dcache,PcodeCacher *pc,AddrSpace *cspc,AddrSpace *uspc,uint4 umask);
virtual void appendBuild(OpTpl *bld,int4 secnum); virtual void appendBuild(OpTpl *bld,int4 secnum);
@ -102,21 +145,31 @@ public:
virtual void appendCrossBuild(OpTpl *bld,int4 secnum); virtual void appendCrossBuild(OpTpl *bld,int4 secnum);
}; };
/// \brief A full SLEIGH engine
///
/// Its provided with a LoadImage of the bytes to be disassembled and
/// a ContextDatabase.
///
/// Assembly is produced via the printAssembly() method, provided with an
/// AssemblyEmit object and an Address.
///
/// P-code is produced via the oneInstruction() method, provided with a PcodeEmit
/// object and an Address.
class Sleigh : public SleighBase { class Sleigh : public SleighBase {
LoadImage *loader; LoadImage *loader; ///< The mapped bytes in the program
ContextDatabase *context_db; ContextDatabase *context_db; ///< Database of context values steering disassembly
ContextCache *cache; ContextCache *cache; ///< Cache of recently used context values
mutable DisassemblyCache *discache; mutable DisassemblyCache *discache; ///< Cache of recently parsed instructions
mutable PcodeCacher pcode_cache; mutable PcodeCacher pcode_cache; ///< Cache of p-code data just prior to emitting
void clearForDelete(void); void clearForDelete(void); ///< Delete the context and disassembly caches
protected: protected:
ParserContext *obtainContext(const Address &addr,int4 state) const; ParserContext *obtainContext(const Address &addr,int4 state) const;
void resolve(ParserContext &pos) const; void resolve(ParserContext &pos) const; ///< Generate a parse tree suitable for disassembly
void resolveHandles(ParserContext &pos) const; void resolveHandles(ParserContext &pos) const; ///< Prepare the parse tree for p-code generation
public: public:
Sleigh(LoadImage *ld,ContextDatabase *c_db); Sleigh(LoadImage *ld,ContextDatabase *c_db); ///< Constructor
virtual ~Sleigh(void); virtual ~Sleigh(void); ///< Destructor
void reset(LoadImage *ld,ContextDatabase *c_db); void reset(LoadImage *ld,ContextDatabase *c_db); ///< Reset the engine for a new program
virtual void initialize(DocumentStorage &store); virtual void initialize(DocumentStorage &store);
virtual void registerContext(const string &name,int4 sbit,int4 ebit); virtual void registerContext(const string &name,int4 sbit,int4 ebit);
virtual void setContextDefault(const string &nm,uintm val); virtual void setContextDefault(const string &nm,uintm val);

View file

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/// \file ghidra_string.hh /// \file string_ghidra.hh
/// \brief Implementation of the StringManager through the ghidra client /// \brief Implementation of the StringManager through the ghidra client
#ifndef __STRING_GHIDRA__ #ifndef __STRING_GHIDRA__

View file

@ -30,10 +30,11 @@ class Architecture;
/// Stores the decoded string until its needed for presentation. /// Stores the decoded string until its needed for presentation.
class StringManager { class StringManager {
protected: protected:
/// \brief String data (a sequence of bytes) stored by StringManager
class StringData { class StringData {
public: public:
bool isTruncated; // \b true if the the string is truncated bool isTruncated; ///< \b true if the the string is truncated
vector<uint1> byteData; // UTF8 encoded string data vector<uint1> byteData; ///< UTF8 encoded string data
}; };
map<Address,StringData> stringMap; ///< Map from address to string data map<Address,StringData> stringMap; ///< Map from address to string data
int4 maximumChars; ///< Maximum characters in a string before truncating int4 maximumChars; ///< Maximum characters in a string before truncating

View file

@ -162,6 +162,12 @@ public:
bool doTrace(void); ///< Trace logical value as far as possible bool doTrace(void); ///< Trace logical value as far as possible
}; };
/// \brief Class for splitting data-flow on \e laned registers
///
/// From a root Varnode and a description of its \e lanes, trace data-flow as far as
/// possible through the function, propagating each lane, using the doTrace() method. Then
/// using the apply() method, data-flow can be split, making each lane in every traced
/// register into an explicit Varnode
class LaneDivide : public TransformManager { class LaneDivide : public TransformManager {
/// \brief Description of a large Varnode that needs to be traced (in the worklist) /// \brief Description of a large Varnode that needs to be traced (in the worklist)
class WorkNode { class WorkNode {

View file

@ -13,470 +13,81 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
/// \file test.cc
/// \brief Unit tests for Ghidra C++ components.
#include "float.hh"
#include "opbehavior.hh"
#include "test.hh" #include "test.hh"
#include "testfunction.hh"
#include <cmath> vector<UnitTest *> UnitTest::tests;
#include <cstdint>
#include <cstring>
#include <limits> /// Run all the tests unless a non-empty set of names is passed in.
#include <vector> /// In which case, only the named tests in the set are run.
/// \param testNames is the set of names
void UnitTest::run(set<string> &testNames)
// utility functions {
float floatFromRawBits(uintb e) { int total = 0;
float f; int passed = 0;
memcpy(&f, &e, 4);
return f;
}
uintb floatToRawBits(float f) { for(auto &t : UnitTest::tests) {
uintb result = 0; if (testNames.size() > 0 && testNames.find(t->name) == testNames.end()) {
memcpy(&result, &f, 4); continue;
return result;
}
double doubleFromRawBits(uintb e) {
double f;
memcpy(&f, &e, 8);
return f;
}
uintb doubleToRawBits(double f) {
uintb result = 0;
memcpy(&result, &f, 8);
return result;
}
// macros to preserve call site
#define ASSERT_FLOAT_ENCODING(f) \
do { \
FloatFormat format(4); \
\
uintb true_encoding = floatToRawBits(f); \
uintb encoding = format.getEncoding(f); \
\
ASSERT_EQUALS(true_encoding, encoding); \
} while (0);
#define ASSERT_DOUBLE_ENCODING(f) \
do { \
FloatFormat format(8); \
\
uintb true_encoding = doubleToRawBits(f); \
uintb encoding = format.getEncoding(f); \
\
ASSERT_EQUALS(true_encoding, encoding); \
} while (0);
//// FloatFormat tests
static std::vector<float> float_test_values{
-0.0f,
+0.0f,
-1.0f,
+1.0f,
-1.234f,
+1.234f,
-std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::min(),
std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::min(),
-std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::quiet_NaN(),
-std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity()
};
static std::vector<int> int_test_values = {
0, -1, 1, 1234, -1234, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()
};
TEST(float_encoding_normal) {
ASSERT_FLOAT_ENCODING(1.234);
ASSERT_FLOAT_ENCODING(-1.234);
}
TEST(double_encoding_normal) {
ASSERT_DOUBLE_ENCODING(1.234);
ASSERT_DOUBLE_ENCODING(-1.234);
}
TEST(float_encoding_nan) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::quiet_NaN());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::quiet_NaN());
}
TEST(double_encoding_nan) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::quiet_NaN());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::quiet_NaN());
}
TEST(float_encoding_subnormal) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::denorm_min());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::denorm_min());
}
TEST(double_encoding_subnormal) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::denorm_min());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::denorm_min());
}
TEST(float_encoding_min_normal) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::min());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::min());
}
TEST(double_encoding_min_normal) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::min());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::min());
}
TEST(float_encoding_infinity) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::infinity());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::infinity());
}
TEST(double_encoding_infinity) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::infinity());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::infinity());
}
TEST(float_midpoint_rounding) {
FloatFormat ff(4);
// IEEE754 recommends "round to nearest even" for binary formats, like single and double
// precision floating point. It rounds to the nearest integer (significand) when unambiguous,
// and to the nearest even on the midpoint.
// There are 52 bits of significand in a double and 23 in a float.
// Below we construct a sequence of double precision values to demonstrate each case
// in rounding,
// d0 - zeros in low 29 bits, round down
// d1 - on the rounding midpoint with integer even integer part, round down
// d2 - just above the midpoint, round up
double d0 = doubleFromRawBits(0x4010000000000000L);
double d1 = doubleFromRawBits(0x4010000010000000L);
double d2 = doubleFromRawBits(0x4010000010000001L);
// d3 - zeros in low 29 bits, round down
// d4 - on the rounding midpoint with integer part odd, round up
// d5 - just above the midpoint, round up
double d3 = doubleFromRawBits(0x4010000020000000L);
double d4 = doubleFromRawBits(0x4010000030000000L);
double d5 = doubleFromRawBits(0x4010000030000001L);
float f0 = (float)d0;
float f1 = (float)d1;
float f2 = (float)d2;
float f3 = (float)d3;
float f4 = (float)d4;
float f5 = (float)d5;
uintb e0 = ff.getEncoding(d0);
uintb e1 = ff.getEncoding(d1);
uintb e2 = ff.getEncoding(d2);
uintb e3 = ff.getEncoding(d3);
uintb e4 = ff.getEncoding(d4);
uintb e5 = ff.getEncoding(d5);
ASSERT_EQUALS(floatToRawBits(f0), e0);
ASSERT_EQUALS(floatToRawBits(f1), e1);
ASSERT_EQUALS(floatToRawBits(f2), e2);
ASSERT_EQUALS(floatToRawBits(f3), e3);
ASSERT_EQUALS(floatToRawBits(f4), e4);
ASSERT_EQUALS(floatToRawBits(f5), e5);
ASSERT_EQUALS(e0, e1);
ASSERT_NOT_EQUALS(e1, e2);
ASSERT_NOT_EQUALS(e3, e4);
ASSERT_EQUALS(e4, e5);
}
// op tests
// generated
TEST(float_opNan) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = isnan(f);
uintb encoding = format.getEncoding(f);
uintb result = format.opNan(encoding);
ASSERT_EQUALS(true_result, result);
} }
} std::cerr << "testing : " << t->name << " ..." << std::endl;
++total;
try {
TEST(float_opNeg) { t->func();
FloatFormat format(4); ++passed;
std::cerr << " passed." << std::endl;
for(float f:float_test_values) { } catch(...) {
uintb true_result = floatToRawBits(-f);
uintb encoding = format.getEncoding(f);
uintb result = format.opNeg(encoding);
ASSERT_EQUALS(true_result, result);
} }
}
std::cerr << "==============================" << std::endl;
std::cerr << passed << "/" << total << " tests passed." << std::endl;
} }
int main(int argc, char **argv) {
bool runUnitTests = true;
bool runDataTests = true;
TEST(float_opAbs) { argc -= 1;
FloatFormat format(4); argv += 1;
set<string> unitTestNames;
for(float f:float_test_values) { set<string> dataTestNames;
uintb true_result = floatToRawBits(abs(f)); string dirname("../datatests");
uintb encoding = format.getEncoding(f); if (argc > 0) {
uintb result = format.opAbs(encoding); string command(argv[0]);
if (command == "-path") {
ASSERT_EQUALS(true_result, result); dirname = argv[1];
runDataTests = true;
argv += 2;
argc -= 2;
} }
} }
if (argc > 0) {
string command(argv[0]);
TEST(float_opSqrt) { if (command == "unittests") {
FloatFormat format(4); runUnitTests = true;
runDataTests = false; // Run only unit tests
for(float f:float_test_values) { unitTestNames.insert(argv + 1,argv + argc);
uintb true_result = floatToRawBits(sqrtf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opSqrt(encoding);
ASSERT_EQUALS(true_result, result);
} }
} else if (command == "datatests") {
runUnitTests = false; // Run only data-tests
runDataTests = true;
TEST(float_opCeil) { dataTestNames.insert(argv + 1,argv + argc);
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(ceilf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opCeil(encoding);
ASSERT_EQUALS(true_result, result);
} }
} else {
cout << "USAGE: ghidra_test [-path <datatestdir>] [[unittests|datatests] [testname1 testname2 ...]]" << endl;
TEST(float_opFloor) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(floorf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opFloor(encoding);
ASSERT_EQUALS(true_result, result);
} }
}
if (runUnitTests)
UnitTest::run(unitTestNames);
if (runDataTests) {
cout << endl << endl;
const char *sleighhomepath = getenv("SLEIGHHOME");
if (sleighhomepath != (const char *)0)
cout << "Using SLEIGHHOME=" << sleighhomepath << endl;
else
cout << "No SLEIGHHOME environment variable" << endl;
startDecompilerLibrary(sleighhomepath);
FunctionTestCollection::runTestCollections(dirname,dataTestNames);
}
} }
TEST(float_opRound) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(roundf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opRound(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opInt2Float_size4) {
FloatFormat format(4);
for(int i:int_test_values) {
uintb true_result = floatToRawBits((float)i);
uintb result = format.opInt2Float(i, 4);
ASSERT_EQUALS(true_result, result);
}
}
// TODO other sized ints
TEST(float_to_double_opFloat2Float) {
FloatFormat format(4);
FloatFormat format8(8);
for(float f:float_test_values) {
uintb true_result = doubleToRawBits((double)f);
uintb encoding = format.getEncoding(f);
uintb result = format.opFloat2Float(encoding, format8);
ASSERT_EQUALS(true_result, result);
}
}
// TODO float2float going the other direction, double_to_float_opFloat2Float
TEST(float_opTrunc_to_int) {
FloatFormat format(4);
FloatFormat format8(8);
for(float f:float_test_values) {
// avoid undefined behavior
if((int64_t)f > std::numeric_limits<int>::max() || (int64_t)f < std::numeric_limits<int>::min())
continue;
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
uintb encoding = format.getEncoding(f);
uintb result = format.opTrunc(encoding, 4);
ASSERT_EQUALS(true_result, result);
}
}
// TODO trunc to other sizes
TEST(float_opEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1==f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opNotEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1!=f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opNotEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opLess) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1<f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opLess(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opLessEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1<=f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opLessEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opAdd) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1+f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opAdd(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opDiv) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1/f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opDiv(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opMult) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1*f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opMult(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opSub) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1-f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opSub(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
// end generated

View file

@ -19,48 +19,59 @@
/// Include this file and any additional headers. Use TEST(testname) as /// Include this file and any additional headers. Use TEST(testname) as
/// prototype in test function definitions. E.g. /// prototype in test function definitions. E.g.
/// test.cc: /// test.cc:
/// #include "float.hh" /// \#include "float.hh"
/// #include "test.hh" /// \#include "test.hh"
/// ///
/// TEST(zero_is_less_than_one) { /// TEST(zero_is_less_than_one) {
/// ASSERT(0.0 < 1.0); /// ASSERT(0.0 < 1.0);
/// } /// }
/// ///
#include <cstdio>
#include <iostream>
#include <sstream>
#include <string>
#include <vector> #include <vector>
#include <set> #include <set>
#include <string>
#include <iostream>
namespace { typedef void (*testfunc_t)(); ///< A unit-test function
struct Test;
typedef void (*testfunc_t)();
std::vector<Test *> tests; /// \brief Simple unit test class
///
/// The macro TEST instantiates this object with a name and function pointer.
/// The static run() method calls all the function pointers of all instantiated
/// objects.
struct UnitTest {
static std::vector<UnitTest *> tests; ///< The collection of test objects
std::string name; ///< Name of the test
testfunc_t func; ///< Call-back function executing the test
struct Test { /// \brief Constructor
std::string name; ///
testfunc_t func; /// \param name is the identifier for the test
/// \param func is a call-back function that executes the test
UnitTest(const std::string &name,testfunc_t func) :
name(name), func(func)
{
tests.push_back(this);
}
Test(const std::string &name, testfunc_t func) : name(name), func(func) { static void run(std::set<std::string> &testNames); ///< Run all the instantiated tests
tests.push_back(this); };
}
};
} // namespace
/// \brief Main unit test macro
#define TEST(testname) \ #define TEST(testname) \
void testname(); \ void testname(); \
Test testname##_obj{ #testname, testname }; \ UnitTest testname##_obj{ #testname, testname }; \
void testname() void testname()
/// \brief Assert that a boolean is \b true for a unit test
#define ASSERT(test) \ #define ASSERT(test) \
if (!(test)) { \ if (!(test)) { \
std::cerr << " failed at " << __FILE__ << ":" << __LINE__ << " asserting \"" << #test << "\"." << std::endl; \ std::cerr << " failed at " << __FILE__ << ":" << __LINE__ << " asserting \"" << #test << "\"." << std::endl; \
throw 0; \ throw 0; \
} }
/// \brief Assert that two values are equal for a unit test
#define ASSERT_EQUALS(a, b) \ #define ASSERT_EQUALS(a, b) \
if ((a) != (b)) { \ if ((a) != (b)) { \
std::stringstream ssa, ssb; \ std::stringstream ssa, ssb; \
@ -71,6 +82,7 @@ namespace {
throw 0; \ throw 0; \
} }
/// \brief Assert that two values are not equal for a unit test
#define ASSERT_NOT_EQUALS(a, b) \ #define ASSERT_NOT_EQUALS(a, b) \
if ((a) == (b)) { \ if ((a) == (b)) { \
std::stringstream ssa, ssb; \ std::stringstream ssa, ssb; \
@ -80,26 +92,3 @@ namespace {
<< " != " << ssb.str() << "\"." << std::endl; \ << " != " << ssb.str() << "\"." << std::endl; \
throw 0; \ throw 0; \
} }
int main(int argc, char **argv) {
int total = 0;
int passed = 0;
std::set<std::string> testnames(argv + 1, argv + argc);
for (auto &t : tests) {
if(testnames.size()>0 && testnames.find(t->name)==testnames.end()) {
continue;
}
std::cerr << "testing : " << t->name << " ..." << std::endl;
++total;
try {
t->func();
++passed;
std::cerr << " passed." << std::endl;
} catch (...) {
}
}
std::cerr << "==============================" << std::endl;
std::cerr << passed << "/" << total << " tests passed." << std::endl;
}

View file

@ -0,0 +1,345 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "testfunction.hh"
#include "filemanage.hh"
void FunctionTestProperty::startTest(void) const
{
count = 0;
}
void FunctionTestProperty::processLine(const string &line) const
{
if (regex_search(line,pattern))
count += 1;
}
bool FunctionTestProperty::endTest(void) const
{
return (count >= minimumMatch && count <= maximumMatch);
}
void FunctionTestProperty::restoreXml(const Element *el)
{
name = el->getAttributeValue("name");
istringstream s1(el->getAttributeValue("min"));
s1 >> minimumMatch;
istringstream s2(el->getAttributeValue("max"));
s2 >> maximumMatch;
pattern = regex(el->getContent());
}
void ConsoleCommands::readLine(string &line)
{
if (pos >= commands.size()) {
line.clear();
return;
}
line = commands[pos];
pos += 1;
}
ConsoleCommands::ConsoleCommands(void) :
IfaceStatus("> ", cout)
{
pos = 0;
IfaceCapability::registerAllCommands(this);
}
void ConsoleCommands::reset(void)
{
commands.clear();
pos = 0;
inerror = false;
done = false;
}
/// \param el is the root \<script> tag
void ConsoleCommands::restoreXml(const Element *el)
{
const List &list(el->getChildren());
List::const_iterator iter;
for(iter=list.begin();iter!=list.end();++iter) {
const Element *subel = *iter;
commands.push_back(subel->getContent());
}
pos = 0;
}
void FunctionTestCollection::clear(void)
{
dcp->clearArchitecture();
testList.clear();
console.reset();
}
/// Instantiate an Architecture object
void FunctionTestCollection::buildProgram(DocumentStorage &docStorage)
{
ArchitectureCapability *capa = ArchitectureCapability::getCapability("xml");
if (capa == (ArchitectureCapability *)0)
throw IfaceExecutionError("Missing XML architecture capability");
dcp->conf = capa->buildArchitecture("test", "", console.optr);
string errmsg;
bool iserror = false;
try {
dcp->conf->init(docStorage);
dcp->conf->readLoaderSymbols("::"); // Read in loader symbols
} catch(XmlError &err) {
errmsg = err.explain;
iserror = true;
} catch(LowlevelError &err) {
errmsg = err.explain;
iserror = true;
}
if (iserror)
throw IfaceExecutionError("Error during architecture initialization: " + errmsg);
}
/// Let each test initialize itself thru its startTest() method
void FunctionTestCollection::startTests(void) const
{
list<FunctionTestProperty>::const_iterator iter;
for(iter=testList.begin();iter!=testList.end();++iter) {
(*iter).startTest();
}
}
/// Each test gets a chance to process a line of output
/// \param line is the given line of output
void FunctionTestCollection::passLineToTests(const string &line) const
{
list<FunctionTestProperty>::const_iterator iter;
for(iter=testList.begin();iter!=testList.end();++iter) {
(*iter).processLine(line);
}
}
/// \brief Do the final evaluation of each test
///
/// This is called after each test has been fed all lines of output.
/// The result of each test is printed to the \e midStream, and then
/// failures are written to the lateStream in order to see a summary.
/// \param midStream is the stream write results to as the test is performed
/// \param lateStream collects failures to display as a summary
void FunctionTestCollection::evaluateTests(ostream &midStream,list<string> &lateStream) const
{
list<FunctionTestProperty>::const_iterator iter;
for(iter=testList.begin();iter!=testList.end();++iter) {
numTestsApplied += 1;
if ((*iter).endTest()) {
midStream << "Success -- " << (*iter).getName() << endl;
numTestsSucceeded += 1;
}
else {
midStream << "FAIL -- " << (*iter).getName() << endl;
lateStream.push_back((*iter).getName());
}
}
}
FunctionTestCollection::FunctionTestCollection(void)
{
dcp = (IfaceDecompData *)console.getData("decompile");
console.setErrorIsDone(true);
numTestsApplied = 0;
numTestsSucceeded = 0;
}
/// Load the architecture based on the discovered \<binaryimage> tag.
/// Collect the script commands and the specific tests.
/// \param filename is the XML file holding the test data
void FunctionTestCollection::loadTest(const string &filename)
{
fileName = filename;
DocumentStorage docStorage;
Document *doc = docStorage.openDocument(filename);
Element *el = doc->getRoot();
if (el->getName() == "decompilertest")
restoreXml(docStorage,el);
else if (el->getName() == "binaryimage")
restoreXmlOldForm(docStorage,el);
else
throw IfaceParseError("Test file " + filename + " has unrecognized XML tag: "+el->getName());
}
void FunctionTestCollection::restoreXml(DocumentStorage &store,const Element *el)
{
clear();
const List &list(el->getChildren());
List::const_iterator iter = list.begin();
bool sawScript = false;
bool sawTests = false;
bool sawProgram = false;
while(iter != list.end()) {
const Element *subel = *iter;
++iter;
if (subel->getName() == "script") {
sawScript = true;
console.restoreXml(subel);
}
else if (subel->getName() == "stringmatch") {
sawTests = true;
testList.emplace_back();
testList.back().restoreXml(subel);
}
else if (subel->getName() == "binaryimage") {
sawProgram = true;
store.registerTag(subel);
buildProgram(store);
}
else
throw IfaceParseError("Unknown tag in <decompiletest>: "+subel->getName());
}
if (!sawScript)
throw IfaceParseError("Did not see <script> tag in <decompiletest>");
if (!sawTests)
throw IfaceParseError("Did not see any <stringmatch> tags in <decompiletest>");
if (!sawProgram)
throw IfaceParseError("No <binaryimage> tag in <decompiletest>");
}
/// Pull the script and tests from a comment in \<binaryimage>
void FunctionTestCollection::restoreXmlOldForm(DocumentStorage &store,const Element *el)
{
clear();
throw IfaceParseError("Old format test not supported");
}
/// Run the script commands on the current program.
/// Collect any bulk output, and run tests over the output.
/// Report test failures back to the caller
/// \param midStream is the output stream to write to during the test
/// \param lateStream collects messages for a final summary
void FunctionTestCollection::runTests(ostream &midStream,list<string> &lateStream)
{
numTestsApplied = 0;
numTestsSucceeded = 0;
ostringstream midBuffer; // Collect command console output
console.optr = &midBuffer;
ostringstream bulkout;
console.fileoptr = &bulkout;
mainloop(&console);
console.optr = &midStream;
console.fileoptr = &midStream;
if (console.isInError()) {
midStream << "Error: Did not apply tests in " << fileName << endl;
midStream << midBuffer.str() << endl;
ostringstream fs;
fs << "Execution failed for " << fileName;
lateStream.push_back(fs.str());
return;
}
string result = bulkout.str();
if (result.size() == 0) {
ostringstream fs;
fs << "No output for " << fileName;
lateStream.push_back(fs.str());
return;
}
startTests();
string::size_type prevpos = 0;
string::size_type pos = result.find_first_of('\n');
while(pos != string::npos) {
string line = result.substr(prevpos,pos - prevpos);
passLineToTests(line);
prevpos = pos + 1;
pos = result.find_first_of('\n',prevpos);
}
if (prevpos != result.size()) {
string line = result.substr(prevpos); // Process final line without a newline char
passLineToTests(line);
}
evaluateTests(midStream, lateStream);
}
/// Run through all XML files in the given directory, processing each in turn.
/// \param dirname is a directory containing the XML test files
/// \param testNames (if not empty) specifies particular tests to run
void FunctionTestCollection::runTestCollections(const string &dirname,set<string> &testNames)
{
FileManage fileManage;
set<string> fullNames;
for(set<string>::iterator iter=testNames.begin();iter!=testNames.end();++iter) {
string val = dirname;
if (dirname.back() != '/')
val += '/';
val += *iter;
fullNames.insert(val);
}
fileManage.addDir2Path(dirname);
vector<string> testFiles;
fileManage.matchList(testFiles,".xml",true);
int4 totalTestsApplied = 0;
int4 totalTestsSucceeded = 0;
list<string> failures;
FunctionTestCollection testCollection;
for(int4 i=0;i<testFiles.size();++i) {
if (!fullNames.empty() && fullNames.find(testFiles[i]) == fullNames.end())
continue;
try {
testCollection.loadTest(testFiles[i]);
testCollection.runTests(cout, failures);
totalTestsApplied += testCollection.getTestsApplied();
totalTestsSucceeded += testCollection.getTestsSucceeded();
} catch(IfaceParseError &err) {
ostringstream fs;
fs << "Error parsing " << testFiles[i] << ": " << err.explain;
cout << fs.str() << endl;
failures.push_back(fs.str());
} catch(IfaceExecutionError &err) {
ostringstream fs;
fs << "Error executing " << testFiles[i] << ": " << err.explain;
cout << fs.str() << endl;
failures.push_back(fs.str());
}
}
cout << endl;
cout << "Total tests applied = " << totalTestsApplied << endl;
cout << "Total passing tests = " << totalTestsSucceeded << endl;
cout << endl;
if (!failures.empty()) {
cout << "Failures: " << endl;
list<string>::const_iterator iter = failures.begin();
for(int4 i=0;i<10;++i) {
cout << " " << *iter << endl;
++iter;
if (iter == failures.end()) break;
}
}
}

View file

@ -0,0 +1,88 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// \file testfunction.hh
/// \brief Framework for decompiler data driven single function tests
#ifndef __TESTFUNCTION__
#define __TESTFUNCTION__
#include "libdecomp.hh"
#include <iostream>
#include <regex>
/// \brief A single property to be searched for in the output of a function decompilation
///
/// This is generally a regular expression run over the characters in the
/// decompiled "source" form of the function.
/// The property may "match" more than once or not at all.
class FunctionTestProperty {
int4 minimumMatch; ///< Minimum number of times property is expected to match
int4 maximumMatch; ///< Maximum number of times property is expected to match
string name; ///< Name of the test, to be printed in test summaries
regex pattern; ///< Regular expression to match against a line of output
mutable uint4 count; ///< Number of times regular expression has been seen
public:
string getName(void) const { return name; } ///< Get the name of the property
void startTest(void) const; ///< Reset "state", counting number of matching lines
void processLine(const string &line) const; ///< Search thru \e line, update state if match found
bool endTest(void) const; ///< Return results of property search
void restoreXml(const Element *el); ///< Reconstruct the property from an XML tag
};
/// \brief A console command run as part of a test sequence
class ConsoleCommands : public IfaceStatus {
vector<string> commands; ///< Sequence of commands
uint4 pos; ///< Position of next command to execute
virtual void readLine(string &line);
public:
ConsoleCommands(void); ///< Constructor
void reset(void); ///< Reset console for a new program
virtual bool isStreamFinished(void) const { return pos == commands.size(); }
void restoreXml(const Element *el); ///< Reconstruct the command from an XML tag
};
/// \brief A collection of tests around a single program/function
///
/// The collection of tests is loaded from a single XML file via loadTest(),
/// and the tests are run by calling runTests().
/// An entire program is loaded and possibly annotated by a series of
/// console command lines. Decompiler output is also triggered by a command,
/// and then the output is scanned for by the test objects (FunctionTestProperty).
/// Results of passed/failed tests are collected. If the command line script
/// does not complete properly, this is considered a special kind of failure.
class FunctionTestCollection {
IfaceDecompData *dcp; ///< Program data for the test collection
string fileName; ///< Name of the file containing test data
list<FunctionTestProperty> testList; ///< List of tests for this collection
ConsoleCommands console; ///< Decompiler console for executing scripts
mutable int4 numTestsApplied; ///< Count of tests that were executed
mutable int4 numTestsSucceeded; ///< Count of tests that passed
void clear(void); ///< Clear any previous architecture and function
void buildProgram(DocumentStorage &store); ///< Build program (Architecture) from \<binaryimage> tag
void startTests(void) const; ///< Initialize each FunctionTestProperty
void passLineToTests(const string &line) const; ///< Let all tests analyze a line of the results
void evaluateTests(ostream &midStream,list<string> &lateStream) const;
public:
FunctionTestCollection(void); ///< Constructor
int4 getTestsApplied(void) const { return numTestsApplied; } ///< Get the number of tests executed
int4 getTestsSucceeded(void) const { return numTestsSucceeded; } ///< Get the number of tests that passed
void loadTest(const string &filename); ///< Load a test program, tests, and script
void restoreXml(DocumentStorage &store,const Element *el); ///< Load tests from a \<decompilertest> tag.
void restoreXmlOldForm(DocumentStorage &store,const Element *el); ///< Load tests from \<binaryimage> tag.
void runTests(ostream &midStream,list<string> &lateStream); ///< Run the script and perform the tests
static void runTestCollections(const string &dirname,set<string> &testNames); ///< Run test files in a whole directory
};
#endif

View file

@ -201,7 +201,7 @@ public:
bool isFloatExtension(void) const { return (pieces.size() == 1); } ///< Does this record extend a float varnode bool isFloatExtension(void) const { return (pieces.size() == 1); } ///< Does this record extend a float varnode
const VarnodeData &getPiece(int4 i) const { return pieces[i]; } ///< Get the i-th piece const VarnodeData &getPiece(int4 i) const { return pieces[i]; } ///< Get the i-th piece
const VarnodeData &getUnified(void) const { return unified; } ///< Get the Varnode whole const VarnodeData &getUnified(void) const { return unified; } ///< Get the Varnode whole
Address getEquivalentAddress(uintb offset,int4 &pos) const; ///< Given offset in \join space, get equivalent address of piece Address getEquivalentAddress(uintb offset,int4 &pos) const; ///< Given offset in \e join space, get equivalent address of piece
bool operator<(const JoinRecord &op2) const; ///< Compare records lexigraphically by pieces bool operator<(const JoinRecord &op2) const; ///< Compare records lexigraphically by pieces
}; };

View file

@ -318,7 +318,7 @@ void Datatype::saveXmlRef(ostream &s) const
/// A CPUI_PTRSUB must act on a pointer data-type where the given offset addresses a component. /// A CPUI_PTRSUB must act on a pointer data-type where the given offset addresses a component.
/// Perform this check. /// Perform this check.
/// \param is the given offset /// \param offset is the given offset
/// \return \b true if \b this is a suitable PTRSUB data-type /// \return \b true if \b this is a suitable PTRSUB data-type
bool Datatype::isPtrsubMatching(uintb offset) const bool Datatype::isPtrsubMatching(uintb offset) const
@ -422,7 +422,7 @@ uint8 Datatype::hashName(const string &nm)
/// The hashing is reversible by feeding the output ID back into this function with the same size. /// The hashing is reversible by feeding the output ID back into this function with the same size.
/// \param id is the given ID to (de)uniquify /// \param id is the given ID to (de)uniquify
/// \param size is the instance size of the structure /// \param size is the instance size of the structure
/// \param return the (de)uniquified id /// \return the (de)uniquified id
uint8 Datatype::hashSize(uint8 id,int4 size) uint8 Datatype::hashSize(uint8 id,int4 size)
{ {

View file

@ -123,6 +123,7 @@ public:
/// Given a specific language and PcodeOp, emit the expression rooted at the operation. /// Given a specific language and PcodeOp, emit the expression rooted at the operation.
/// \param lng is the PrintLanguage to emit /// \param lng is the PrintLanguage to emit
/// \param op is the specific PcodeOp /// \param op is the specific PcodeOp
/// \param readOp is the PcodeOp consuming the output (or null)
virtual void push(PrintLanguage *lng,const PcodeOp *op,const PcodeOp *readOp) const=0; virtual void push(PrintLanguage *lng,const PcodeOp *op,const PcodeOp *readOp) const=0;
/// \brief Print (for debugging purposes) \b this specific PcodeOp to the stream /// \brief Print (for debugging purposes) \b this specific PcodeOp to the stream

View file

@ -22,7 +22,7 @@
/// properly, in which case the union of the two ranges can exist without /// properly, in which case the union of the two ranges can exist without
/// destroying data-type information. /// destroying data-type information.
/// \param b is the range to reconcile with \b this /// \param b is the range to reconcile with \b this
/// \param \b true if the data-type information can be reconciled /// \return \b true if the data-type information can be reconciled
bool RangeHint::reconcile(const RangeHint *b) const bool RangeHint::reconcile(const RangeHint *b) const
{ {

View file

@ -399,7 +399,7 @@ void Varnode::setSymbolEntry(SymbolEntry *entry)
/// This used when there is a constant address reference to the Symbol and the Varnode holds the /// This used when there is a constant address reference to the Symbol and the Varnode holds the
/// reference, not the actual value of the Symbol. /// reference, not the actual value of the Symbol.
/// \param entry is a mapping to the given Symbol /// \param entry is a mapping to the given Symbol
/// \off is the byte offset into the Symbol of the reference /// \param off is the byte offset into the Symbol of the reference
void Varnode::setSymbolReference(SymbolEntry *entry,int4 off) void Varnode::setSymbolReference(SymbolEntry *entry,int4 off)
{ {

View file

@ -0,0 +1,30 @@
<decompilertest>
<binaryimage arch="MIPS:LE:32:default:default">
<!--
Contrived example of a LOAD off of a pointer that eventually
collapses to an address in a volatile region. The value returned
by the LOAD is unused. This test makes sure the side-effects of
the volatile read are still present.
-->
<bytechunk space="ram" offset="0xbfc05558" readonly="true">
c1bf053c5cdfa58c
e8ffbd2701a0023c5491428c1400bfaf
120001240000a090
8618f00f00000000
0800e0038001bd27
</bytechunk>
<bytechunk space="ram" offset="0xbfc0df5c" readonly="true">
00100000
</bytechunk>
<symbol space="ram" offset="0xbfc05558" name="deadvolatile"/>
</binaryimage>
<script>
<com>option readonly on</com>
<com>volatile [ram,0x1000,16]</com>
<com>lo fu deadvolatile</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Dead Volatile #1" min="1" max="1">read_volatile.*0x1000</stringmatch>
</decompilertest>

View file

@ -0,0 +1,70 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
A contrived function with different float and double encodings.
-->
<bytechunk space="ram" offset="0x4004c7" readonly="true">
554889e5f30f100539
030000f30f1105550b2000f30f10052d
030000f30f1105490b2000f30f100521
030000f30f11053d0b2000f30f100515
030000f30f1105310b2000f30f100509
030000660fefc9f30f5ec1f30f11051d
0b2000660fefc0660fefc9f30f5ec1f3
0f11050d0b2000f30f1005e1020000f3
0f1105010b2000f20f1005d9020000f2
0f1105f90a2000f20f1005d1020000f2
0f1105f10a2000f20f1005c9020000f2
0f1105e90a2000f20f1005c1020000f2
0f1105e10a2000f20f1005b902000066
0fefc9f20f5ec1f20f1105d10a200066
0fefc0660fefc9f20f5ec1f20f1105c5
0a2000f20f100595020000f20f1105bd
0a2000905dc3
</bytechunk>
<bytechunk space="ram" offset="0x40080c" readonly="true">
abaaaa3e
000000406f1283babd3786350000803f
24d4523600000000555555555555e53f
0000000000000040fca9f1d24d6250bf
bbbdd7d9df7cdb3d000000000000f03f
7e7480d3845aca3e
</bytechunk>
<symbol space="ram" offset="0x4004c7" name="establish"/>
</binaryimage>
<script>
<com>map addr r0x601030 float4 floatv1</com>
<com>map addr r0x601034 float4 floatv2</com>
<com>map addr r0x601038 float4 floatv3</com>
<com>map addr r0x60103c float4 floatv4</com>
<com>map addr r0x601040 float4 floatv5</com>
<com>map addr r0x601044 float4 floatv6</com>
<com>map addr r0x601048 float4 floatv7</com>
<com>map addr r0x601050 float8 double1</com>
<com>map addr r0x601058 float8 double2</com>
<com>map addr r0x601060 float8 double3</com>
<com>map addr r0x601068 float8 double4</com>
<com>map addr r0x601070 float8 double5</com>
<com>map addr r0x601078 float8 double6</com>
<com>map addr r0x601080 float8 double7</com>
<com>option readonly on</com>
<com>lo fu establish</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Float print #1" min="1" max="1">floatv1 = 0.3333333;</stringmatch>
<stringmatch name="Float print #2" min="1" max="1">floatv2 = 2.0;</stringmatch>
<stringmatch name="Float print #3" min="1" max="1">floatv3 = -0.001;</stringmatch>
<stringmatch name="Float print #4" min="1" max="1">floatv4 = 1e-06;</stringmatch>
<stringmatch name="Float print #5" min="1" max="1">floatv5 = INFINITY;</stringmatch>
<stringmatch name="Float print #6" min="1" max="1">floatv6 = -NAN;</stringmatch>
<stringmatch name="Float print #7" min="1" max="1">floatv7 = 3.141592e-06;</stringmatch>
<stringmatch name="Float print #8" min="1" max="1">double1 = 0.6666666666666666;</stringmatch>
<stringmatch name="Float print #9" min="1" max="1">double2 = 2.0;</stringmatch>
<stringmatch name="Float print #10" min="1" max="1">double3 = -0.001;</stringmatch>
<stringmatch name="Float print #11" min="1" max="1">double4 = 1e-10;</stringmatch>
<stringmatch name="Float print #12" min="1" max="1">double5 = INFINITY;</stringmatch>
<stringmatch name="Float print #13" min="1" max="1">double6 = -NAN;</stringmatch>
<stringmatch name="Float print #14" min="1" max="1">double7 = 3.141592653589793e-06;</stringmatch>
</decompilertest>

View file

@ -0,0 +1,22 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Basic for-loop. Contrived example.
-->
<bytechunk space="ram" offset="0x400517" readonly="true">
554889e5534883ec18
897decbb00000000eb0dbf20084000e8
fcfeffff83c3013b5dec7cee904883c4
185b5dc3
</bytechunk>
<symbol space="ram" offset="0x400517" name="forloop1"/>
</binaryimage>
<script>
<com>lo fu forloop1</com>
<com>parse line extern void forloop1(int4 max);</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="For-loop #1" min="1" max="1">for.*iVar1 = 0; iVar1 &lt; max; iVar1 = iVar1 \+ 1</stringmatch>match>
</decompilertest>

View file

@ -0,0 +1,31 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
For-loop where the iterator statement LOADs the next value. Contrived example.
-->
<bytechunk space="ram" offset="0x4005fb" readonly="true">
554889e548
83ec2048897de8c745fc00000000eb10
488b45e8488b4018488945e88345fc01
48837de80075e98b45fc89c6bf440840
00b800000000e805feffff90c9c3
</bytechunk>
<bytechunk space="ram" offset="0x400844" readonly="true">
436f756e74203d2025640a00
</bytechunk>
<symbol space="ram" offset="0x4005fb" name="forloop_loaditer"/>
<symbol space="ram" offset="0x400440" name="printf"/>
</binaryimage>
<script>
<com>parse line extern void printf(char *,...);</com>
<com>parse line extern void forloop_loaditer(int4 **ptr);</com>
<com>lo fu forloop_loaditer</com>
<com>map addr s0xfffffffffffffff4 int4 count</com>
<com>map addr s0xffffffffffffffe0 int4 **loopvar</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="For-loop iterator load #1" min="1" max="1">for \(loopvar = ptr; loopvar != \(int4 \*\*\)0x0; loopvar = \(int4 \*\*\)loopvar\[3\]\)</stringmatch>
<stringmatch name="For-loop iterator load #2" min="1" max="1">count = count \+ 1;</stringmatch>
</decompilertest>

View file

@ -0,0 +1,30 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
For loop where the increment statement must cross special operations. Contrived example.
-->
<bytechunk space="ram" offset="0x4005bb" readonly="true">
554889e553
4883ec18897dec488975e0bb03000000
eb1c83c301488b45e0c7000a00000048
8345e004bf38084000e842feffff3b5d
ec7cdf904883c4185b5dc3
</bytechunk>
<bytechunk space="ram" offset="0x400838" readonly="true">
4d616b6520612063616c6c00
</bytechunk>
<symbol space="ram" offset="0x4005bb" name="forloop_thruspecial"/>
<symbol space="ram" offset="0x400430" name="puts"/>
</binaryimage>
<script>
<com>parse line extern void puts(char *);</com>
<com>parse line extern void forloop_thruspecial(int4 max,int4 *ptr);</com>
<com>lo fu forloop_thruspecial</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="For-loop thru special #1" min="1" max="1">for.*iVar1 = 3; iVar1 &lt; max; iVar1 = iVar1 \+ 1</stringmatch>
<stringmatch name="For-loop thru special #2" min="1" max="1">\*piStack.* = 10;</stringmatch>
<stringmatch name="For-loop thru special #3" min="1" max="1">puts.*Make a call.*;</stringmatch>
</decompilertest>

View file

@ -0,0 +1,31 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
For-loop where the loop variable is used elsewhere in the loop body. Contrived example.
-->
<bytechunk space="ram" offset="0x400544" readonly="true">
554889e5534883ec18897dec
bb00000000eb2e89d883e00385c07511
bf26084000b800000000e8d1feffffeb
1189debf2e084000b800000000e8befe
ffff83c3013b5dec7ccd904883c4185b
5dc3
</bytechunk>
<bytechunk space="ram" offset="0x400826" readonly="true">
30206d6f6420340076616c203d2025640a00
</bytechunk>
<symbol space="ram" offset="0x400544" name="forloop_loopvarused"/>
<symbol space="ram" offset="0x400440" name="printf"/>
</binaryimage>
<script>
<com>parse line extern void printf(char *,...);</com>
<com>parse line extern void forloop_loopvarused(int4 max);</com>
<com>lo fu forloop_loopvarused</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="For-loop var used #1" min="1" max="1">for.*uVar1 = 0;.*uVar1 &lt; max; uVar1 = uVar1 \+ 1</stringmatch>
<stringmatch name="For-loop var used #2" min="1" max="1">if \(\(uVar1 &amp; 3\) == 0\)</stringmatch>
<stringmatch name="For-loop var used #3" min="1" max="1">val = %d.*uVar1</stringmatch>
</decompilertest>

View file

@ -0,0 +1,24 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
For-loop where the loop var may be additionally incremented in the loop body. Contrived example.
-->
<bytechunk space="ram" offset="0x400592" readonly="true">
554889e553897df4bb00000000eb
0c837df40a7e0383c30183c3018b057d
0a200039c37cea905b5dc3
</bytechunk>
<symbol space="ram" offset="0x400592" name="forloop_withskip"/>
</binaryimage>
<script>
<com>map addr r0x601030 int4 globvar</com>
<com>parse line extern void forloop_withskip(int4 val);</com>
<com>lo fu forloop_withskip</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="For-loop with skip #1" min="1" max="1">for.*iVar1 = 0; iVar1 &lt; globvar; iVar1 = iVar1 \+ 1</stringmatch>
<stringmatch name="For-loop with skip #2" min="1" max="1">if \(10 &lt; val\)</stringmatch>
<stringmatch name="For-loop with skip #3" min="1" max="1">iVar1 = iVar1 \+ 1;</stringmatch>
</decompilertest>

View file

@ -0,0 +1,47 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
A contrived function with different loops, where we put comments on instructions
comprising the loop conditions. The comments should get moved to a separate line
and not get printed in the middle of the condition.
-->
<bytechunk space="ram" offset="0x1006e4" readonly="true">
554889e54883ec20897dec89
75e88955e4894de08b45ec83f8097f1c
837de8647e168b45e43de8030000750c
488d3d0d020000e864feffffc745fc00
000000eb28488d3d04020000e84ffeff
ff488d45ec4889c7e88dffffff488d45
e04889c7e881ffffff8345fc018b45e0
3dc70000007f378b45ec83f8137f2f81
7dfcc70000007ebdeb24488d3dc80100
00e80afeffff488d45ec4889c7e848ff
ffff488d45e04889c7e83cffffff8b45
ec83f8630f9ec28b45e083f80a0f9fc0
21d084c075c48b45ec83c00a8945ec48
8d3d8e010000e8c5fdffff8b45ec89c6
488d3d85010000b800000000e8bffdff
ff488d45ec4889c7e8edfeffff488d45
e44889c7e8e1feffff8b45ec3dcf0700
007f088b45e483f81d7eab8b45ec89c6
488d3d49010000b800000000e87ffdff
ff90c9c3
</bytechunk>
<symbol space="ram" offset="0x1006e4" name="loopcomment"/>
</binaryimage>
<script>
<com>lo fu loopcomment</com>
<com>comment instr r0x100704 ifcomment</com>
<com>comment instr r0x10075d forcomment</com>
<com>comment instr r0x100791 whilecomment</com>
<com>comment instr r0x1007f9 dowhilecomment</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Loop comment #1" min="1" max="1">^ */\* ifcomment \*/ *$</stringmatch>
<stringmatch name="Loop comment #2" min="1" max="1">^ */\* forcomment \*/ *$</stringmatch>
<stringmatch name="Loop comment #3" min="1" max="1">^ */\* whilecomment \*/ *$</stringmatch>
<stringmatch name="Loop comment #4" min="1" max="1">^ */\* dowhilecomment \*/ *$</stringmatch>
<stringmatch name="Loop comment #5" min="4" max="4">.*/\*</stringmatch>
</decompilertest>

View file

@ -0,0 +1,29 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
A routine that uses multiple symbols with the same name. The decompiler should
detect the name collisions and append appropriate namespace tokens.
-->
<bytechunk space="ram" offset="0x400537" readonly="true">
554889e5897dfc8b45
fc83c0018905e60a20008b55fc89d0c1
e00201d001c08905d80a20008b15d60a
20008b45fc01d05dc3
</bytechunk>
<symbol space="ram" offset="0x400537" name="a::b::assign_vals"/>
</binaryimage>
<script>
<com>map addr r0x601030 int4 spam</com>
<com>map addr r0x601034 int4 a::spam</com>
<com>map addr r0x601038 int4 c::spam</com>
<com>parse line extern int4 a::b::assign_vals(int4 spam);</com>
<com>lo fu a::b::assign_vals</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Namespace #1" min="1" max="1">a::b::assign_vals\(int4 spam\)</stringmatch>
<stringmatch name="Namespace #2" min="1" max="1">^ ::spam = spam \+ 1;</stringmatch>
<stringmatch name="Namespace #3" min="1" max="1">^ a::spam = spam \* 10;</stringmatch>
<stringmatch name="Namespace #4" min="1" max="1">return spam \+ c::spam;</stringmatch>
</decompilertest>

View file

@ -0,0 +1,25 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Hand coded routine where the offset into a structure to reach an array field
is contained inside the index expression for the array. The decompiler must
distribute the constant multiple scaling the array index to "see" the offset.
-->
<bytechunk space="ram" offset="0x400517" readonly="true">
554889e5488d441602
488d04878b005dc3
</bytechunk>
<symbol space="ram" offset="0x400517" name="readstruct"/>
</binaryimage>
<script>
<com>parse line struct twostruct { int4 field1; int4 array[5]; };</com>
<com>parse line extern int4 readstruct(twostruct *ptr,int8 a,int8 b);</com>
<com>lo fu readstruct</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Nested offset #1" min="1" max="1">return ptr-&gt;array\[b \+ a\];</stringmatch>
<stringmatch name="Nested offset #2" min="0" max="0">field</stringmatch>
<stringmatch name="Nested offset #3" min="0" max="0">\* 4</stringmatch>
</decompilertest>

View file

@ -0,0 +1,35 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
A loop that should not recover as a for-loop as the loop variable is potentially aliased after the iterate statement.
Contrived example.
-->
<bytechunk space="ram" offset="0x400690" readonly="true">
554889e54883ec20897decc745f00100
0000c745f400000000c745f802000000
c745fc03000000eb298b45f483c00189
45f4488d45f04889c7e8adffffff8b45
f489c6bf5d084000b800000000e85efd
ffff8b45f43945ec7fcf90c9c3
</bytechunk>
<bytechunk space="ram" offset="0x40085d" readonly="true">
56616c203d2025640a00
</bytechunk>
<symbol space="ram" offset="0x400690" name="noforloop_alias"/>
<symbol space="ram" offset="0x400440" name="printf"/>
<symbol space="ram" offset="0x40067b" name="might_change"/>
</binaryimage>
<script>
<com>parse line extern void printf(char *,...);</com>
<com>parse line extern void noforloop_alias(int4 max);</com>
<com>lo fu noforloop_alias</com>
<com>map addr s0xffffffffffffffe8 int4 i[4]</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="No for-loop alias #1" min="1" max="1">while \(i\[1\] &lt; max\)</stringmatch>
<stringmatch name="No for-loop alias #2" min="1" max="1">i\[1\] = i\[1\] \+ 1;</stringmatch>
<stringmatch name="No for-loop alias #3" min="4" max="4">i\[[0-3]\] = [0-3];</stringmatch>
<stringmatch name="No for-loop alias #4" min="1" max="1">might_change\(i\);</stringmatch>
</decompilertest>

View file

@ -0,0 +1,27 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
A loop that should not recover as a for-loop as the loop variable is global and may change
in a subfunction after the iteration statement.
-->
<bytechunk space="ram" offset="0x4006ed" readonly="true">
554889
e54883ec0848897df8c7052d09200000
000000eb1b8b052509200083c0018905
1c092000488b45f84889c7e85bffffff
8b050a09200083f8097eda90c9c3
</bytechunk>
<symbol space="ram" offset="0x4006ed" name="noforloop_globcall"/>
</binaryimage>
<script>
<com>parse line extern void noforloop_globcall(int4 *ptr);</com>
<com>map addr r0x601030 int4 globvar</com>
<com>lo fu noforloop_globcall</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="No for-loop global call #1" min="1" max="1">while \(globvar &lt; 10\)</stringmatch>
<stringmatch name="No for-loop global call #2" min="1" max="1">globvar = 0;</stringmatch>
<stringmatch name="No for-loop global call #3" min="1" max="1">globvar = globvar \+ 1;</stringmatch>
</decompilertest>

View file

@ -0,0 +1,32 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
A loop that should not recover as a for-loop as the loop variable is used after the iterate statement. Contrived example.
-->
<bytechunk space="ram" offset="0x40063e" readonly="true">
5548
89e5534883ec18897decbb0a000000eb
1d89debf50084000b800000000e8defd
ffff83c3016bc3648905c20920003b5d
ec7cde904883c4185b5dc3
</bytechunk>
<bytechunk space="ram" offset="0x400850" readonly="true">
4265666f7265203d2025640a00
</bytechunk>
<symbol space="ram" offset="0x40063e" name="noforloop_iterused"/>
<symbol space="ram" offset="0x400440" name="printf"/>
</binaryimage>
<script>
<com>parse line extern void printf(char *,...);</com>
<com>parse line extern void noforloop_iterused(int4 max);</com>
<com>map addr r0x601030 int4 globvar</com>
<com>lo fu noforloop_iterused</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="No for-loop iterator used #1" min="1" max="1">while \(.*uVar1 &lt; max\)</stringmatch>
<stringmatch name="No for-loop iterator used #2" min="1" max="1">uVar1 = 10;</stringmatch>
<stringmatch name="No for-loop iterator used #3" min="1" max="1">uVar1 = uVar1 \+ 1;</stringmatch>
<stringmatch name="No for-loop iterator used #4" min="1" max="1">globvar = uVar1 \* 100;</stringmatch>
</decompilertest>

View file

@ -0,0 +1,27 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived routine that indexes into an array as a subfield of a structure. The
original source code had a negative offset added to the index, which got folded into
the stack offset for the start of the array. The offset is easily confused with the
offset of the "firstfield" of the structure, but the decompiler should figure it out
because of the clear array indexing.
-->
<bytechunk space="ram" offset="0x100000" readonly="true">
534889fb4881ec900000004889e7e8ed
0f00008b049c4881c4900000005bc3
</bytechunk>
<symbol space="ram" offset="0x100000" name="access_array1"/>
<symbol space="ram" offset="0x101000" name="populate_mystruct"/>
</binaryimage>
<script>
<com>parse line struct mystruct { int4 firstfield; int4 array[32]; };</com>
<com>parse line extern void populate_mystruct(mystruct *ptr);</com>
<com>lo fu access_array1</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Offset array #1" min="1" max="1">return mStack.*array\[param_1 \+ -2\]</stringmatch>
<stringmatch name="Offset array #2" min="0" max="0">firstfield</stringmatch>
</decompilertest>

View file

@ -0,0 +1,20 @@
<decompilertest>
<binaryimage arch="x86:LE:32:default:gcc">
<bytechunk space="ram" offset="0x80662e0" readonly="true">
5589e58b55080fb60284c0750eeb17
</bytechunk>
<bytechunk space="ram" offset="0x80662f0" readonly="true">
0fb6420184c0740e83c20183e8303c09
76ee5d31c0c35db801000000c3
</bytechunk>
<symbol space="ram" offset="0x80662e0" name="promote_compare"/>
</binaryimage>
<script>
<com>lo fu promote_compare</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Promotion on compare #1" min="1" max="1">9.*uint1..cVar.*0x30</stringmatch>
<stringmatch name="Promotion on compare #2" min="1" max="1">uint1</stringmatch>
</decompilertest>

View file

@ -0,0 +1,24 @@
<decompilertest>
<binaryimage arch="68000:BE:32:MC68020:default">
<!--
Simple example of a read from a volatile region, where the "value" is not
used, but the read may have side-effects that the decompiler should see.
-->
<bytechunk space="ram" offset="0x484">
1028001e4200114000141140
000a114000141140000a1140000a4e73
</bytechunk>
<symbol space="ram" offset="0x484" name="iofunc"/>
</binaryimage>
<script>
<com>volatile [ram,0x210000,64]</com>
<com>map addr r0x210000 int1 NVRAM[32]</com>
<com>set track A0 0x210000 r0x484 r0x485</com>
<com>lo fu iofunc</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Read Volatile #1" min="1" max="1">read_volatile.*NVRAM.*30</stringmatch>
<stringmatch name="Read Volatile #2" min="5" max="5">write_volatile.*NVRAM</stringmatch>
</decompilertest>

View file

@ -0,0 +1,32 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived example of reading and writing to a two dimensional array.
-->
<bytechunk space="ram" offset="0x400590" readonly="true">
554889e5897dec8975e8c745fc140000
00eb758b05b73f2c008b55ec4863ca48
988b55fc4863d24869f0d00700004889
d048c1e0024801d04801c04801f04801
c88b0485601060008945f88b057f3f2c
008b55e84863ca48988b55fc4863d248
69f0d00700004889d048c1e0024801d0
4801c04801f0488d14088b45f8890495
601060008345fc01837dfc1d7e858b45
f85dc3
</bytechunk>
<symbol space="ram" offset="0x400590" name="array_access"/>
</binaryimage>
<script>
<com>map addr r0x6c4560 int4 globindex</com>
<com>map addr r0x601060 int4 myarray[100][200][10]</com>
<com>parse line extern int4 array_access(int4 valin,int4 valout);</com>
<com>lo fu array_access</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Three dimension #1" min="1" max="1">iStack16 = myarray\[globindex\]\[iStack12\]\[valin\];</stringmatch>
<stringmatch name="Three dimension #2" min="1" max="1">myarray\[globindex\]\[iStack12\]\[valout\] = iStack16;</stringmatch>
<stringmatch name="Three dimension #3" min="0" max="0"> \* </stringmatch>
</decompilertest>

View file

@ -0,0 +1,29 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived example of reading and writing to a two dimensional array.
-->
<bytechunk space="ram" offset="0x400568" readonly="true">
554889e5897dec89
75e88b05240b20008b55ec4863ca4863
d04889d048c1e0024801d04801c88b04
85601060008945fc8b05fe0a20008b55
fc8d4a0a8b55e84863f24863d04889d0
48c1e0024801d04801f0890c85601060
008b45fc5dc3
</bytechunk>
<symbol space="ram" offset="0x400568" name="array_access"/>
</binaryimage>
<script>
<com>map addr r0x60109c int4 globindex</com>
<com>map addr r0x601060 int4 myarray[3][5]</com>
<com>parse line extern int4 array_access(int4 valin,int4 valout);</com>
<com>lo fu array_access</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Two dimension #1" min="1" max="1">iVar. = myarray\[globindex\]\[valin\];</stringmatch>
<stringmatch name="Two dimension #2" min="1" max="1">myarray\[globindex\]\[valout\] = iVar. \+ 10;</stringmatch>
<stringmatch name="Two dimension #3" min="0" max="0"> \* </stringmatch>
</decompilertest>

View file

@ -0,0 +1,27 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<!--
Contrived routine that indexes into an array as a subfield of a structure. The
original source code had a negative offset added to the index, which got folded into
the stack offset for the start of the array. The offset pulls the base reference
entirely out of the mapped structure, so the decompiler must search forward in
the local map to find it.
-->
<bytechunk space="ram" offset="0x100000" readonly="true">
534889fb4881ec900000004889e7e8ed
0f00008b449cf84881c4900000005bc3
</bytechunk>
<symbol space="ram" offset="0x100000" name="access_array1"/>
<symbol space="ram" offset="0x101000" name="populate_mystruct"/>
</binaryimage>
<script>
<com>parse line struct mystruct { int8 firstfield; int4 array[32]; };</com>
<com>parse line extern void populate_mystruct(mystruct *ptr);</com>
<com>lo fu access_array1</com>
<com>decompile</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Wayoff array #1" min="1" max="1">return mStack.*array\[param_1 \+ -4\]</stringmatch>
<stringmatch name="Wayoff array #2" min="0" max="0">firstfield</stringmatch>
</decompilertest>

View file

@ -0,0 +1,482 @@
/* ###
* IP: GHIDRA
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// \file test.cc
/// \brief Unit tests for Ghidra C++ components.
#include "float.hh"
#include "opbehavior.hh"
#include "test.hh"
#include <cmath>
#include <cstdint>
#include <cstring>
#include <limits>
#include <vector>
// utility functions
float floatFromRawBits(uintb e) {
float f;
memcpy(&f, &e, 4);
return f;
}
uintb floatToRawBits(float f) {
uintb result = 0;
memcpy(&result, &f, 4);
return result;
}
double doubleFromRawBits(uintb e) {
double f;
memcpy(&f, &e, 8);
return f;
}
uintb doubleToRawBits(double f) {
uintb result = 0;
memcpy(&result, &f, 8);
return result;
}
// macros to preserve call site
#define ASSERT_FLOAT_ENCODING(f) \
do { \
FloatFormat format(4); \
\
uintb true_encoding = floatToRawBits(f); \
uintb encoding = format.getEncoding(f); \
\
ASSERT_EQUALS(true_encoding, encoding); \
} while (0);
#define ASSERT_DOUBLE_ENCODING(f) \
do { \
FloatFormat format(8); \
\
uintb true_encoding = doubleToRawBits(f); \
uintb encoding = format.getEncoding(f); \
\
ASSERT_EQUALS(true_encoding, encoding); \
} while (0);
//// FloatFormat tests
static std::vector<float> float_test_values{
-0.0f,
+0.0f,
-1.0f,
+1.0f,
-1.234f,
+1.234f,
-std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::min(),
std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
-std::numeric_limits<float>::min(),
-std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::quiet_NaN(),
-std::numeric_limits<float>::infinity(),
std::numeric_limits<float>::infinity()
};
static std::vector<int> int_test_values = {
0, -1, 1, 1234, -1234, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()
};
TEST(float_encoding_normal) {
ASSERT_FLOAT_ENCODING(1.234);
ASSERT_FLOAT_ENCODING(-1.234);
}
TEST(double_encoding_normal) {
ASSERT_DOUBLE_ENCODING(1.234);
ASSERT_DOUBLE_ENCODING(-1.234);
}
TEST(float_encoding_nan) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::quiet_NaN());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::quiet_NaN());
}
TEST(double_encoding_nan) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::quiet_NaN());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::quiet_NaN());
}
TEST(float_encoding_subnormal) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::denorm_min());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::denorm_min());
}
TEST(double_encoding_subnormal) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::denorm_min());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::denorm_min());
}
TEST(float_encoding_min_normal) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::min());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::min());
}
TEST(double_encoding_min_normal) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::min());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::min());
}
TEST(float_encoding_infinity) {
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::infinity());
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::infinity());
}
TEST(double_encoding_infinity) {
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::infinity());
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::infinity());
}
TEST(float_midpoint_rounding) {
FloatFormat ff(4);
// IEEE754 recommends "round to nearest even" for binary formats, like single and double
// precision floating point. It rounds to the nearest integer (significand) when unambiguous,
// and to the nearest even on the midpoint.
// There are 52 bits of significand in a double and 23 in a float.
// Below we construct a sequence of double precision values to demonstrate each case
// in rounding,
// d0 - zeros in low 29 bits, round down
// d1 - on the rounding midpoint with integer even integer part, round down
// d2 - just above the midpoint, round up
double d0 = doubleFromRawBits(0x4010000000000000L);
double d1 = doubleFromRawBits(0x4010000010000000L);
double d2 = doubleFromRawBits(0x4010000010000001L);
// d3 - zeros in low 29 bits, round down
// d4 - on the rounding midpoint with integer part odd, round up
// d5 - just above the midpoint, round up
double d3 = doubleFromRawBits(0x4010000020000000L);
double d4 = doubleFromRawBits(0x4010000030000000L);
double d5 = doubleFromRawBits(0x4010000030000001L);
float f0 = (float)d0;
float f1 = (float)d1;
float f2 = (float)d2;
float f3 = (float)d3;
float f4 = (float)d4;
float f5 = (float)d5;
uintb e0 = ff.getEncoding(d0);
uintb e1 = ff.getEncoding(d1);
uintb e2 = ff.getEncoding(d2);
uintb e3 = ff.getEncoding(d3);
uintb e4 = ff.getEncoding(d4);
uintb e5 = ff.getEncoding(d5);
ASSERT_EQUALS(floatToRawBits(f0), e0);
ASSERT_EQUALS(floatToRawBits(f1), e1);
ASSERT_EQUALS(floatToRawBits(f2), e2);
ASSERT_EQUALS(floatToRawBits(f3), e3);
ASSERT_EQUALS(floatToRawBits(f4), e4);
ASSERT_EQUALS(floatToRawBits(f5), e5);
ASSERT_EQUALS(e0, e1);
ASSERT_NOT_EQUALS(e1, e2);
ASSERT_NOT_EQUALS(e3, e4);
ASSERT_EQUALS(e4, e5);
}
// op tests
// generated
TEST(float_opNan) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = isnan(f);
uintb encoding = format.getEncoding(f);
uintb result = format.opNan(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opNeg) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(-f);
uintb encoding = format.getEncoding(f);
uintb result = format.opNeg(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opAbs) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(abs(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opAbs(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opSqrt) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(sqrtf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opSqrt(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opCeil) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(ceilf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opCeil(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opFloor) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(floorf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opFloor(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opRound) {
FloatFormat format(4);
for(float f:float_test_values) {
uintb true_result = floatToRawBits(roundf(f));
uintb encoding = format.getEncoding(f);
uintb result = format.opRound(encoding);
ASSERT_EQUALS(true_result, result);
}
}
TEST(float_opInt2Float_size4) {
FloatFormat format(4);
for(int i:int_test_values) {
uintb true_result = floatToRawBits((float)i);
uintb result = format.opInt2Float(i, 4);
ASSERT_EQUALS(true_result, result);
}
}
// TODO other sized ints
TEST(float_to_double_opFloat2Float) {
FloatFormat format(4);
FloatFormat format8(8);
for(float f:float_test_values) {
uintb true_result = doubleToRawBits((double)f);
uintb encoding = format.getEncoding(f);
uintb result = format.opFloat2Float(encoding, format8);
ASSERT_EQUALS(true_result, result);
}
}
// TODO float2float going the other direction, double_to_float_opFloat2Float
TEST(float_opTrunc_to_int) {
FloatFormat format(4);
FloatFormat format8(8);
for(float f:float_test_values) {
// avoid undefined behavior
if((int64_t)f > std::numeric_limits<int>::max() || (int64_t)f < std::numeric_limits<int>::min())
continue;
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
uintb encoding = format.getEncoding(f);
uintb result = format.opTrunc(encoding, 4);
ASSERT_EQUALS(true_result, result);
}
}
// TODO trunc to other sizes
TEST(float_opEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1==f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opNotEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1!=f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opNotEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opLess) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1<f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opLess(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opLessEqual) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = (f1<=f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opLessEqual(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opAdd) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1+f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opAdd(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opDiv) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1/f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opDiv(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opMult) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1*f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opMult(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
TEST(float_opSub) {
FloatFormat format(4);
for(float f1:float_test_values) {
uintb encoding1 = format.getEncoding(f1);
for(float f2:float_test_values) {
uintb true_result = floatToRawBits(f1-f2);
uintb encoding2 = format.getEncoding(f2);
uintb result = format.opSub(encoding1, encoding2);
ASSERT_EQUALS(true_result, result);
}
}
}
// end generated