mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/caheckman_DecompilerTestFramework'
This commit is contained in:
commit
09478efc2a
62 changed files with 3126 additions and 1204 deletions
|
@ -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|
|
||||||
|
|
|
@ -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>
|
|
@ -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
|
||||||
|
|
|
@ -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 $@.$$$$
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"; }
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
345
Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc
Normal file
345
Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.cc
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh
Normal file
88
Ghidra/Features/Decompiler/src/decompile/cpp/testfunction.hh
Normal 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
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 < max; iVar1 = iVar1 \+ 1</stringmatch>match>
|
||||||
|
</decompilertest>
|
|
@ -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>
|
|
@ -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 < 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>
|
|
@ -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 < max; uVar1 = uVar1 \+ 1</stringmatch>
|
||||||
|
<stringmatch name="For-loop var used #2" min="1" max="1">if \(\(uVar1 & 3\) == 0\)</stringmatch>
|
||||||
|
<stringmatch name="For-loop var used #3" min="1" max="1">val = %d.*uVar1</stringmatch>
|
||||||
|
</decompilertest>
|
|
@ -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 < globvar; iVar1 = iVar1 \+ 1</stringmatch>
|
||||||
|
<stringmatch name="For-loop with skip #2" min="1" max="1">if \(10 < val\)</stringmatch>
|
||||||
|
<stringmatch name="For-loop with skip #3" min="1" max="1">iVar1 = iVar1 \+ 1;</stringmatch>
|
||||||
|
</decompilertest>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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->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>
|
|
@ -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\] < 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>
|
|
@ -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 < 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>
|
|
@ -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 < 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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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
|
Loading…
Add table
Add a link
Reference in a new issue