avast / avast/retdec

Provide support for large types

Open
#367 0 comments 0 reactions 0 assignees View on GitHub
C-bin2llvmir enhancement P-output
Dominant language
C++
Stars
8.6k
Forks
1k
PR merge metrics
No merged PRs in 30d

Description

Provide support for types that have size larger than word size of an architecture. Work with values of such type is specific for different ABIs. For instance on architectures that support passing arguments in registers (ARM, MIPS) are values passed in register pairs. Current implementation does not apply for such situations and thus some docompilations may result in incorrect code.

There are situation where by luck is generated correct code, for example on arm decompilation of such code...
```
int fun(uint64_t a, uint64_t b)
{
return a+b;
}

int main()
{
return fun(0, 1);
}
```
...would result in...
```
int32_t fun(int32_t a1, int32_t a2, int32_t a3, int32_t a4, int32_t a5, int32_t a6) {
return a3 + a1;
}

int main(int argc, char ** argv) {
return fun(0, 0, 1, 0, 0, 0);
}
```
...but if the input was...
```
int fun(uint32_t a, uint64_t b, uint32_t c)
{
return a+b+c;
}

int main()
{
return fun(0, 1, 2);
}
```
...then the decompilation would generate code...
```
int32_t fun(int32_t a1) {
int32_t v1;
return g1 + a1 + v1;
}

int main(int argc, char ** argv) {
g1 = 1;
return fun(0);
}
```
...which is incorrect. This is result of incorrect parameter analysis which does not take to account register pairs, that are used as parameter. For example on 32bit ARM are 64bit values stored in register pairs A0:A1, A2:A3, but the first register of a pair must have even number. The same behavior can be observer on MIPS as well.

Another situation is when function has return type of size larger than word size of architecture. Different ABIs use different techinques to return large values, for example register pairs. Current implementation of return type analysis does not expect that the result can be in something else than a one register. So code like this:

```
uint64_t fun()
{
return 4294967296;
}

int main()
{
uint64_t a = fun();

if (a > 0)
return 0;

return 1;
}
```
will result for example on ARM, MIPS and x86 in passing value in two registers but the result of decompilation of binaries is around the same on each architecture:

```
int32_t fun(void) {
return 0;
}

int main(int argc, char ** argv) {
int32_t v1 = fun();
return ((int32_t)((int64_t)v1 / 0x100000000) | v1) == 0;
}
```

Contributor guide

No contributing guide indexed for this repository

Research direction

No source files, tests, or entry points are named. Start by tracing the parameter and return type analysis described in the ARM, MIPS, and x86 examples, then compare the generated decompilations with the original functions. Done means large arguments and return values follow each target ABI, including register pairs and alignment, without producing incorrect code.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
compilers, reverse-engineering
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.