Skip to content

Commit

Permalink
Feature: add support for [[reko::characteristics]] in Characteristics…
Browse files Browse the repository at this point in the history
…Parser (#1260)
  • Loading branch information
uxmal committed Apr 28, 2023
1 parent 0438481 commit 7b39b14
Show file tree
Hide file tree
Showing 9 changed files with 247 additions and 181 deletions.
18 changes: 17 additions & 1 deletion doc/guide/usermetadata.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,24 @@ exit(int exit_code);
```
Reko is sensitive to this characteristic and will use it during both the scanning phase and the data flow analysis phase. C++ 11 defines the standard attribute `[[noreturn]]` which Reko treats as having the same semantics as `[[reko::characteristics({terminates:true})]]`.

#### Specifying a custom varargs parser
If a procedure requires varargs support, it can be specified by using the `varargs` characteristic:
```C++
[[reko::address("00123400")]]
[[reko::characteristics({varargs:"MyExtension.VarargsHandler"})]]
printf(char * format, ...);
```
The parameter to the `varargs` characteristics is the fully qualified type name of a class that implements the
`Reko.Core.Analysis.IVarargsFormatParser` interface. Varargs format parsers for the well-established `*printf`
and `*scanf` function families are provided by the `Reko.Libraries.Libc.PrintfFormatParser,Reko.Libraries.Libc`
and `Reko.Libraries.Libc.ScanfFormatParser,Reko.Libraries.Libc` classes, respectively.
### Specifying service procedures
A service procedure is a procedure where one or more of the input parameters are used to choose different functionality. The MS-DOS `int 21h` service vector is a well-known example of this technique. To specify a service procedure based on an interrupt or system call, use the `[[reko::service]]` attribute. This attribute specifies the interrupt or system call number, and the register(s) used to select a particular function.
A service procedure is a procedure where one or more of the input parameters are used to choose different services. The MS-DOS `int 21h` service vector is a well-known example of this technique, using the `ah`, `al` and other registers to select various
operating environment services.
To specify a service procedure based on an interrupt or system call, use the `[[reko::service]]` attribute. This attribute specifies the interrupt or system call number, and the register(s) used to select a particular function.
The following example is a vastly simplified representation of the 32-bit i386 Linux `int 80h` service vector:
```C++
Expand Down
9 changes: 7 additions & 2 deletions src/Core/Hll/C/CharacteristicsParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public ProcedureCharacteristics Parse()
return DefaultProcedureCharacteristics.Instance;
bool terminates = false;
bool alloca = false;
string? sVarargs = null;
do
{
var charName = Expect<string>(CTokenType.Id, "identifier");
Expand All @@ -53,18 +54,22 @@ public ProcedureCharacteristics Parse()
terminates = (sTerminates == "true");
break;
case "alloca":
var sAlloca = Expect <string>(CTokenType.Id, "true or false");
var sAlloca = Expect<string>(CTokenType.Id, "true or false");
alloca = (sAlloca != "false" && sAlloca != "0");
break;
case "varargs":
sVarargs = Expect<string>(CTokenType.StringLiteral, "string literal");
break;
default:
throw new CParserException($"Unexpected characteristic '{charName}'.");
throw new CParserException($"Unexpected procedure characteristic '{charName}'.");
}
} while (PeekAndDiscard(CTokenType.Comma));
Expect(CTokenType.RBrace, "}");
return new ProcedureCharacteristics
{
Terminates = terminates,
IsAlloca = alloca,
VarargsParserClass = sVarargs,
};
}

Expand Down
5 changes: 1 addition & 4 deletions src/Decompiler/Scanning/VarargsFormatScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,10 +209,7 @@ private static bool VarargsParserSet(ProcedureCharacteristics chr)
var svc = services.RequireService<IPluginLoaderService>();
var type = svc.GetType(varargsParserTypename);
if (type is null)
throw new TypeLoadException(
string.Format(
"Unable to load {0} varargs parser.",
chr.VarargsParserClass));
throw new TypeLoadException($"Unable to load {chr.VarargsParserClass} varargs parser.");
var varargsParser = (IVarargsFormatParser)Activator.CreateInstance(
type,
program,
Expand Down
11 changes: 11 additions & 0 deletions src/UnitTests/Core/Hll/C/CharacteristicsParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

using NUnit.Framework;
using Reko.Core.Hll.C;
using Reko.ImageLoaders.Elf.Relocators;
using System.IO;

namespace Reko.UnitTests.Core.Serialization
Expand Down Expand Up @@ -67,5 +68,15 @@ public void Chp_Parse_alloca()

Assert.IsTrue(result.IsAlloca);
}

[Test]
public void Chp_Parse_varargs_class()
{
Given_Parser("[[reko::characteristics({varargs:\"My.Custom.Class\"})]]");

var result = chp.Parse();

Assert.AreEqual("My.Custom.Class", result.VarargsParserClass);
}
}
}
13 changes: 12 additions & 1 deletion src/UserInterfaces/AvaloniaUI/ViewModels/ProcedureDialogModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public ProcedureDialogModel(Program program, UserProcedure userProc)
this.IsAllocator = userProc.Characteristics.Allocator;
this.IsAlloca = userProc.Characteristics.IsAlloca;
this.Terminates = userProc.Characteristics.Terminates;
this.VarargsFormatParser = userProc.Characteristics.VarargsParserClass;
OnSignatureChanged();
}

Expand Down Expand Up @@ -77,7 +78,10 @@ public ProcedureDialogModel(Program program, UserProcedure userProc)
{
Allocator = this.IsAllocator,
Terminates = this.Terminates,
IsAlloca = this.IsAlloca
IsAlloca = this.IsAlloca,
VarargsParserClass = !string.IsNullOrWhiteSpace(this.VarargsFormatParser)
? this.VarargsFormatParser
: null,
}
};
return procNew;
Expand Down Expand Up @@ -130,6 +134,13 @@ public bool Terminates
}
private bool terminates;

public string? VarargsFormatParser
{
get => varargsFormatParser;
set => this.RaiseAndSetIfChanged(ref varargsFormatParser, value);
}
private string? varargsFormatParser;

private void OnSignatureChanged()
{
// Attempt to parse the signature.
Expand Down
1 change: 1 addition & 0 deletions src/UserInterfaces/AvaloniaUI/Views/ProcedureDialog.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<CheckBox Name="chkAlloca" IsChecked="{Binding IsAlloca}">
_Allocates stack memory (like alloca)
</CheckBox>
<TextBox Name="txtVarargsFormatParser" Text="{Binding VarargsFormatParser}" />
</StackPanel>
</Border>
<StackPanel HorizontalAlignment="Right"
Expand Down

0 comments on commit 7b39b14

Please sign in to comment.