The structure of test.sh scripts.
For a more complete overview of the entire testing suite see the scripts/abi_artifact_generator/end_to_end.md in the revng-qa repository.
Only the the scripts provided in this directory are explained here.
graph TB
linkStyle default stroke: #888
classDef black fill: #fff, stroke: #000, stroke-width: 2px
classDef red fill: #fff, stroke: #f55, stroke-width: 2px
classDef blue fill: #fff, stroke: #55f, stroke-width: 2px
classDef green fill: #fff, stroke: #090, stroke-width: 2px
classDef purple fill: #fff, stroke: #a2a, stroke-width: 2px
fb(functions binary)
rb(runner binary)
mb(mmap'ed binary)
class fb,rb,mb red
reference_binary.yml(reference_binary.yml)
downgraded_reference_binary.yml(downgraded_reference_binary.yml)
upgraded_downgraded_reference_binary.yml(upgraded_downgraded_reference_binary.yml)
downgraded_upgraded_downgraded_reference_binary.yml(downgraded_upgraded_downgraded_reference_binary.yml)
class reference_binary.yml,downgraded_reference_binary.yml,upgraded_downgraded_reference_binary.yml,downgraded_upgraded_downgraded_reference_binary.yml black
yaml("stdout (yaml)")
class yaml blue
check-compatibility-with-abi(check-compatibility-with-abi)
ensure-rft-equivalence(ensure-rft-equivalence)
class check-compatibility-with-abi,ensure-rft-equivalence purple
fb -- "[import-binary + some extra processing]" --> reference_binary.yml
reference_binary.yml -- "[convert-functions-to-raw]" --> downgraded_reference_binary.yml
downgraded_reference_binary.yml -- "[convert-functions-to-cabi]" --> upgraded_downgraded_reference_binary.yml
upgraded_downgraded_reference_binary.yml -- "[convert-functions-to-raw]" --> downgraded_upgraded_downgraded_reference_binary.yml
rb -- "[run under qemu]" --> yaml
mb -- "[argument]" --> rb
reference_binary.yml --> check-compatibility-with-abi
downgraded_reference_binary.yml --> check-compatibility-with-abi
upgraded_downgraded_reference_binary.yml --> check-compatibility-with-abi
downgraded_upgraded_downgraded_reference_binary.yml --> check-compatibility-with-abi
yaml --> check-compatibility-with-abi
downgraded_reference_binary.yml --> ensure-rft-equivalence
downgraded_upgraded_downgraded_reference_binary.yml --> ensure-rft-equivalence
(graphviz)
strict digraph {
"functions binary" [color=red]
"mmap'ed binary" [color=red]
"runner binary" [color=red]
"functions binary" -> "reference_binary.yml" [label="[import-binary + some extra processing]"]
"reference_binary.yml" -> "downgraded_reference_binary.yml" [label="[convert-functions-to-raw]"]
"downgraded_reference_binary.yml" -> "upgraded_downgraded_reference_binary.yml" [label="[convert-functions-to-cabi]"]
"upgraded_downgraded_reference_binary.yml" -> "downgraded_upgraded_downgraded_reference_binary.yml" [label="[convert-functions-to-raw]"]
"runner binary" -> "stdout (yaml)" [label="[run under qemu]"]
"mmap'ed binary" -> "runner binary" [label="[argument]"]
"check-compatibility-with-abi" [color=purple]
"reference_binary.yml" -> "check-compatibility-with-abi"
"downgraded_reference_binary.yml" -> "check-compatibility-with-abi"
"upgraded_downgraded_reference_binary.yml" -> "check-compatibility-with-abi"
"downgraded_upgraded_downgraded_reference_binary.yml" -> "check-compatibility-with-abi"
"stdout (yaml)" -> "check-compatibility-with-abi"
"ensure-rft-equivalence" [color=purple]
"downgraded_reference_binary.yml" -> "ensure-rft-equivalence"
"downgraded_upgraded_downgraded_reference_binary.yml" -> "ensure-rft-equivalence"
"stdout (yaml)" [color=lightblue]
}
Legend:
- red - binary output of a compiler.
- blue - output of the
"runner binary". - purple - tools from
revng/tests/tools/abi. - black - intermediate model files.
verify-abi-test-binaries.sh
The order of the operations done by the pipeline can be found in verify-abi-test-binaries.sh. Let me elaborate a bit further on every command:
revng analyze import-binary
The abi testing reference binary is imported into a model using the normal means. The code for generating this "pure" is located in the revng-qa repository.
python3 replace-abi.py
Because sometimes it's impossible to detect ABI correctly based on dwarf data alone, and because having the right ABI is really important for the integrity of this test, if for any CABI-FT inside the binary the recorded ABI doesn't exactly match the expected ABI, this script overrides it.
Let's call the model after this step a reference_binary.yml.
revng analyze convert-functions-to-raw
This analysis converts every CABI function within the input model (reference_binary.yml) into its "raw" representation. The result is saved into (downgraded_reference_binary.yml)
revng analyze convert-functions-to-cabi
This pass tries to "upgrade" every "raw" function into its CABI representation knowing the ABI it uses. The result is written to upgraded_downgraded_reference_binary.yml.
revng analyze convert-functions-to-raw
The same pass is run again to obtain one more set of "raw" functions. Those get written into downgraded_upgraded_downgraded_reference_binary.yml.
revng check-compatibility-with-abi
As the last step, normal abi verification is run on every single model produced during the course of this test. This makes sure that at no point during the test, the "current" model was incompatible with the real binary it's supposed to represent. The information used to verify comes from running the specially crafted binary that abuses the compiler to generate tests in such a way that we can surely tell where a specific value was placed as a part of the ABI "handshake". To read about it in more details, see the explanation about how it is generated in revng-qa
The artifact provides information that can be used to verify whether a given argument COULD have been passed using a given register. There's no certainty: since all the values are randomly generated, collisions are likely. That's why each function has multiple iterations per artifact. Also that means that artifact is ineffective for restoring the original function signature, but it's good enough to verify whether a known signature corresponds to the factual state.
revng check-compatibility-with-abi tool goes through every function present in both the artifact and the model it was passed and and checks the conformity of the data within the model. For example, if the function within the model was fiddled with (its type was changed, for example) and was changed in a non-backwards-compatible way, this could be a good way to detect the problem.
revng ensure-rft-equivalence
This tool is used to compare downgraded_reference_binary.yml and downgraded_upgraded_downgraded_reference_binary.yml. If no information was lost during these conversions, the models are to be the same.
Sadly, there are cases where revng model diff is not good enough to verify that two instances of a model are the same. Mostly because it relies on the type IDs. So even if there are two identical types generated in different places (they have different IDs because of that), the diff detects these "changes" and fails the check.
To counteract that, use a name-first approach. And since there are quirks, it's easier to provide specialized tool (namely revng ensure-rft-equivalence) which scouts two separate model files for the functions with a matching names and then takes extra care comparing them semantically.