mirror of https://github.com/x64dbg/btparser
(Somewhat) correctly handle function declarations and function pointer types
This commit is contained in:
parent
c1c257ce7a
commit
a87d84cc5b
|
@ -15,8 +15,11 @@ TypeManager::TypeManager()
|
|||
{
|
||||
primitivesizes[p] = size;
|
||||
auto splits = StringUtils::Split(n, ',');
|
||||
for(const auto & split : splits)
|
||||
for (const auto& split : splits)
|
||||
{
|
||||
primitivenames.emplace(p, split);
|
||||
addType("", p, split);
|
||||
}
|
||||
};
|
||||
p("int8_t,int8,char,byte,bool,signed char", Int8, sizeof(char));
|
||||
p("uint8_t,uint8,uchar,unsigned char,ubyte", Uint8, sizeof(unsigned char));
|
||||
|
@ -27,14 +30,20 @@ TypeManager::TypeManager()
|
|||
p("int64_t,int64,long long", Int64, sizeof(long long));
|
||||
p("uint64_t,uint64,unsigned long long", Uint64, sizeof(unsigned long long));
|
||||
p("dsint", Dsint, sizeof(void*));
|
||||
p("duint,size_t", Duint, sizeof(void*));
|
||||
p("size_t,duint", Duint, sizeof(void*));
|
||||
p("float", Float, sizeof(float));
|
||||
p("double", Double, sizeof(double));
|
||||
p("ptr,void*", Pointer, sizeof(void*));
|
||||
p("void*,ptr", Pointer, sizeof(void*));
|
||||
p("char*,const char*", PtrString, sizeof(char*));
|
||||
p("wchar_t*,const wchar_t*", PtrWString, sizeof(wchar_t*));
|
||||
}
|
||||
|
||||
std::string Types::TypeManager::PrimitiveName(Primitive primitive)
|
||||
{
|
||||
auto found = primitivenames.find(primitive);
|
||||
return found == primitivenames.end() ? "" : found->second;
|
||||
}
|
||||
|
||||
bool TypeManager::AddType(const std::string & owner, const std::string & type, const std::string & name)
|
||||
{
|
||||
if(owner.empty())
|
||||
|
@ -46,6 +55,32 @@ bool TypeManager::AddType(const std::string & owner, const std::string & type, c
|
|||
return addType(owner, found->second.primitive, name);
|
||||
}
|
||||
|
||||
bool Types::TypeManager::AddEnum(const std::string& owner, const std::string& name, const std::string& type)
|
||||
{
|
||||
Enum e;
|
||||
e.name = name;
|
||||
e.owner = owner;
|
||||
auto found = types.find(type);
|
||||
if (found == types.end())
|
||||
return false;
|
||||
if (!found->second.pointto.empty())
|
||||
return false;
|
||||
switch (found->second.primitive)
|
||||
{
|
||||
case Void:
|
||||
case Float:
|
||||
case Double:
|
||||
case Pointer:
|
||||
case PtrString:
|
||||
case PtrWString:
|
||||
return false;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
e.type = found->second.primitive;
|
||||
return addEnum(e);
|
||||
}
|
||||
|
||||
bool TypeManager::AddStruct(const std::string & owner, const std::string & name)
|
||||
{
|
||||
StructUnion s;
|
||||
|
@ -122,7 +157,25 @@ bool TypeManager::AppendMember(const std::string & type, const std::string & nam
|
|||
return AddMember(laststruct, type, name, arrsize, offset);
|
||||
}
|
||||
|
||||
bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn)
|
||||
bool Types::TypeManager::AddEnumerator(const std::string& enumType, const std::string& name, uint64_t value)
|
||||
{
|
||||
auto found = enums.find(enumType);
|
||||
if (found == enums.end())
|
||||
return false;
|
||||
auto& e = found->second;
|
||||
for (const auto& v : e.values)
|
||||
{
|
||||
if (v.name == name || v.value == value)
|
||||
return false;
|
||||
}
|
||||
EnumValue v;
|
||||
v.name = name;
|
||||
v.value = value;
|
||||
e.values.push_back(v);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TypeManager::AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv, bool noreturn, bool typeonly)
|
||||
{
|
||||
auto found = functions.find(name);
|
||||
if(found != functions.end() || name.empty() || owner.empty())
|
||||
|
@ -136,16 +189,16 @@ bool TypeManager::AddFunction(const std::string & owner, const std::string & nam
|
|||
f.rettype = rettype;
|
||||
f.callconv = callconv;
|
||||
f.noreturn = noreturn;
|
||||
functions.insert({f.name, f});
|
||||
functions.emplace(f.name, f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TypeManager::AddArg(const std::string & function, const std::string & type, const std::string & name)
|
||||
{
|
||||
if(!isDefined(type) && !validPtr(type))
|
||||
return false;
|
||||
auto found = functions.find(function);
|
||||
if(found == functions.end() || function.empty() || name.empty() || !isDefined(type))
|
||||
if (found == functions.end() || name.empty())
|
||||
return false;
|
||||
if(type != "..." && !isDefined(type) && !validPtr(type))
|
||||
return false;
|
||||
lastfunction = function;
|
||||
Member arg;
|
||||
|
@ -255,7 +308,7 @@ static void enumType(const std::unordered_map<K, V> & map, std::vector<TypeManag
|
|||
}
|
||||
}
|
||||
|
||||
void TypeManager::Enum(std::vector<Summary> & typeList) const
|
||||
void TypeManager::Enumerate(std::vector<Summary> & typeList) const
|
||||
{
|
||||
typeList.clear();
|
||||
enumType(types, typeList);
|
||||
|
@ -310,7 +363,7 @@ static bool mapContains(const std::unordered_map<K, V> & map, const K & k)
|
|||
|
||||
bool TypeManager::isDefined(const std::string & id) const
|
||||
{
|
||||
return mapContains(types, id) || mapContains(structs, id);
|
||||
return mapContains(types, id) || mapContains(structs, id) || mapContains(enums, id) || mapContains(functions, id);
|
||||
}
|
||||
|
||||
bool TypeManager::validPtr(const std::string & id)
|
||||
|
@ -337,16 +390,22 @@ bool TypeManager::addStructUnion(const StructUnion & s)
|
|||
laststruct = s.name;
|
||||
if(s.owner.empty() || s.name.empty() || isDefined(s.name))
|
||||
return false;
|
||||
structs.insert({s.name, s});
|
||||
return true;
|
||||
return structs.emplace(s.name, s).second;
|
||||
}
|
||||
|
||||
bool Types::TypeManager::addEnum(const Enum& e)
|
||||
{
|
||||
lastenum = e.name;
|
||||
if (e.owner.empty() || e.name.empty() || isDefined(e.name))
|
||||
return false;
|
||||
return enums.emplace(e.name, e).second;
|
||||
}
|
||||
|
||||
bool TypeManager::addType(const Type & t)
|
||||
{
|
||||
if(t.name.empty() || isDefined(t.name))
|
||||
return false;
|
||||
types.insert({t.name, t});
|
||||
return true;
|
||||
return types.emplace(t.name, t).second;
|
||||
}
|
||||
|
||||
bool TypeManager::addType(const std::string & owner, Primitive primitive, const std::string & name, const std::string & pointto)
|
||||
|
@ -483,7 +542,7 @@ bool RemoveType(const std::string & type)
|
|||
void EnumTypes(std::vector<Types::TypeManager::Summary> & typeList)
|
||||
{
|
||||
SHARED_ACQUIRE(LockTypeManager);
|
||||
return typeManager.Enum(typeList);
|
||||
return typeManager.Enumerate(typeList);
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -602,7 +661,6 @@ static void loadFunctions(const JSON froot, std::vector<Function> & functions)
|
|||
void LoadModel(const std::string & owner, Model & model)
|
||||
{
|
||||
//Add all base struct/union types first to avoid errors later
|
||||
// TODO: handle forward declarations
|
||||
for(auto & su : model.structUnions)
|
||||
{
|
||||
auto success = su.isunion ? typeManager.AddUnion(owner, su.name) : typeManager.AddStruct(owner, su.name);
|
||||
|
@ -626,15 +684,32 @@ void LoadModel(const std::string & owner, Model & model)
|
|||
}
|
||||
|
||||
//Add enums
|
||||
for (auto& enumm : model.enums)
|
||||
for (auto& kv : model.enums)
|
||||
{
|
||||
__debugbreak();
|
||||
auto& e = kv.first;
|
||||
const auto& etype = kv.second;
|
||||
auto success = typeManager.AddEnum(owner, e.name, etype);
|
||||
if (!success)
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add enum %s;\n"), e.name.c_str());
|
||||
e.name.clear(); // signal error
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& v : e.values)
|
||||
{
|
||||
if (!typeManager.AddEnumerator(e.name, v.name, v.value))
|
||||
{
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add enum member %s.%s = %llu;\n"), e.name.c_str(), v.name.c_str(), v.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Add base function types to avoid errors later
|
||||
for(auto & function : model.functions)
|
||||
{
|
||||
auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn);
|
||||
auto success = typeManager.AddFunction(owner, function.name, function.rettype, function.callconv, function.noreturn, function.typeonly);
|
||||
if(!success)
|
||||
{
|
||||
//TODO properly handle errors
|
||||
|
@ -664,13 +739,16 @@ void LoadModel(const std::string & owner, Model & model)
|
|||
{
|
||||
if(function.name.empty()) //skip error-signalled functions
|
||||
continue;
|
||||
for(auto & arg : function.args)
|
||||
for (size_t i = 0; i < function.args.size(); i++)
|
||||
{
|
||||
auto& arg = function.args[i];
|
||||
if (arg.name.empty())
|
||||
arg.name = "__unnamed" + std::to_string(i);
|
||||
auto success = typeManager.AddArg(function.name, arg.type, arg.name);
|
||||
if(!success)
|
||||
{
|
||||
//TODO properly handle errors
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add argument %s %s.%s;\n"), arg.type.c_str(), function.name.c_str(), arg.name.c_str());
|
||||
dprintf(QT_TRANSLATE_NOOP("DBG", "Failed to add argument %s[%zu]: %s %s;\n"), function.name.c_str(), i, arg.type.c_str(), arg.name.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,14 +23,15 @@ namespace Types
|
|||
Double,
|
||||
Pointer,
|
||||
PtrString, //char* (null-terminated)
|
||||
PtrWString //wchar_t* (null-terminated)
|
||||
PtrWString, //wchar_t* (null-terminated)
|
||||
Varargs,
|
||||
};
|
||||
|
||||
struct Type
|
||||
{
|
||||
std::string owner; //Type owner
|
||||
std::string name; //Type identifier.
|
||||
std::string pointto; //Type identifier of *Type
|
||||
std::string pointto; //Type identifier of *Type (empty when the type is a primitive type)
|
||||
Primitive primitive = Void; //Primitive type.
|
||||
int size = 0; //Size in bytes.
|
||||
};
|
||||
|
@ -64,7 +65,7 @@ namespace Types
|
|||
std::string owner; // Enum owner
|
||||
std::string name; // Enum name
|
||||
std::vector<EnumValue> values; // Enum values
|
||||
Type type; // Enum value type
|
||||
Primitive type; // Enum value type
|
||||
};
|
||||
|
||||
enum CallingConvention
|
||||
|
@ -83,6 +84,7 @@ namespace Types
|
|||
std::string rettype; //Function return type
|
||||
CallingConvention callconv = DefaultDecl; //Function calling convention
|
||||
bool noreturn = false; //Function does not return (ExitProcess, _exit)
|
||||
bool typeonly = false; //Function is only used as a type (the name is based on where it's used)
|
||||
std::vector<Member> args; //Function arguments
|
||||
};
|
||||
|
||||
|
@ -107,32 +109,39 @@ namespace Types
|
|||
};
|
||||
|
||||
explicit TypeManager();
|
||||
std::string PrimitiveName(Primitive primitive);
|
||||
bool AddType(const std::string & owner, const std::string & type, const std::string & name);
|
||||
bool AddEnum(const std::string& owner, const std::string& name, const std::string & type);
|
||||
bool AddStruct(const std::string & owner, const std::string & name);
|
||||
bool AddUnion(const std::string & owner, const std::string & name);
|
||||
bool AddMember(const std::string & parent, const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
|
||||
bool AppendMember(const std::string & type, const std::string & name, int arrsize = 0, int offset = -1);
|
||||
bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false);
|
||||
bool AddFunction(const std::string & owner, const std::string & name, const std::string & rettype, CallingConvention callconv = Cdecl, bool noreturn = false, bool typeonly = false);
|
||||
bool AddEnumerator(const std::string& enumType, const std::string& name, uint64_t value);
|
||||
bool AddArg(const std::string & function, const std::string & type, const std::string & name);
|
||||
bool AppendArg(const std::string & type, const std::string & name);
|
||||
int Sizeof(const std::string & type) const;
|
||||
bool Visit(const std::string & type, const std::string & name, Visitor & visitor) const;
|
||||
void Clear(const std::string & owner = "");
|
||||
bool RemoveType(const std::string & type);
|
||||
void Enum(std::vector<Summary> & typeList) const;
|
||||
void Enumerate(std::vector<Summary> & typeList) const;
|
||||
std::string StructUnionPtrType(const std::string & pointto) const;
|
||||
|
||||
private:
|
||||
std::unordered_map<Primitive, int> primitivesizes;
|
||||
std::unordered_map<Primitive, std::string> primitivenames;
|
||||
std::unordered_map<std::string, Type> types;
|
||||
std::unordered_map<std::string, Types::Enum> enums;
|
||||
std::unordered_map<std::string, StructUnion> structs;
|
||||
std::unordered_map<std::string, Function> functions;
|
||||
std::string lastenum;
|
||||
std::string laststruct;
|
||||
std::string lastfunction;
|
||||
|
||||
bool isDefined(const std::string & id) const;
|
||||
bool validPtr(const std::string & id);
|
||||
bool addStructUnion(const StructUnion & s);
|
||||
bool addEnum(const Enum& e);
|
||||
bool addType(const std::string & owner, Primitive primitive, const std::string & name, const std::string & pointto = "");
|
||||
bool addType(const Type & t);
|
||||
bool visitMember(const Member & root, Visitor & visitor) const;
|
||||
|
@ -141,7 +150,7 @@ namespace Types
|
|||
struct Model
|
||||
{
|
||||
std::vector<Member> types;
|
||||
std::vector<Enum> enums;
|
||||
std::vector<std::pair<Enum, std::string>> enums;
|
||||
std::vector<StructUnion> structUnions;
|
||||
std::vector<Function> functions;
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ struct Parser
|
|||
std::vector<Lexer::TokenState> tokens;
|
||||
size_t index = 0;
|
||||
Model model;
|
||||
std::unordered_map<std::string, size_t> structUnions;
|
||||
|
||||
Parser(const std::string& code, const std::string& owner, std::vector<std::string>& errors)
|
||||
: owner(owner), errors(errors)
|
||||
|
@ -296,20 +297,17 @@ struct Parser
|
|||
|
||||
// Function pointer argument to a function
|
||||
Function subfn;
|
||||
subfn.typeonly = true;
|
||||
if(!parseFunction(tlist, subfn, true))
|
||||
return false;
|
||||
|
||||
// TODO: store this somewhere
|
||||
|
||||
|
||||
|
||||
// Create fake tokens
|
||||
// TODO: handle this properly somehow
|
||||
auto typeToken = tlist.back();
|
||||
typeToken.Token = Lexer::tok_identifier;
|
||||
typeToken.IdentifierStr = fn.name + "_" + subfn.name + "_fnptr";
|
||||
|
||||
printf("TODO function pointer: %s %s\n", typeToken.IdentifierStr.c_str(), subfn.name.c_str());
|
||||
|
||||
subfn.name = typeToken.IdentifierStr;
|
||||
model.functions.push_back(subfn);
|
||||
|
||||
auto nameToken = startToken;
|
||||
nameToken.Token = Lexer::tok_identifier;
|
||||
|
@ -455,8 +453,9 @@ struct Parser
|
|||
index++;
|
||||
|
||||
// Function pointer type
|
||||
Function fn;
|
||||
if (!parseFunction(tlist, fn, true))
|
||||
Function subfn;
|
||||
subfn.typeonly = true;
|
||||
if (!parseFunction(tlist, subfn, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -468,9 +467,22 @@ struct Parser
|
|||
}
|
||||
eatSemic();
|
||||
|
||||
// TODO: put the function somewhere
|
||||
// Create fake tokens
|
||||
auto typeToken = tlist.back();
|
||||
typeToken.Token = Lexer::tok_identifier;
|
||||
typeToken.IdentifierStr = su.name + "_" + subfn.name + "_fnptr";
|
||||
|
||||
printf("TODO function pointer: %s\n", fn.name.c_str());
|
||||
subfn.name = typeToken.IdentifierStr;
|
||||
model.functions.push_back(subfn);
|
||||
|
||||
auto nameToken = startToken;
|
||||
nameToken.Token = Lexer::tok_identifier;
|
||||
nameToken.IdentifierStr = subfn.name;
|
||||
|
||||
// Replace the return type with the fake tokens
|
||||
tlist.clear();
|
||||
tlist.push_back(typeToken);
|
||||
tlist.push_back(nameToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -515,6 +527,7 @@ struct Parser
|
|||
|
||||
bool parseStructUnion()
|
||||
{
|
||||
auto startToken = curToken();
|
||||
if (isToken(Lexer::tok_struct) || isToken(Lexer::tok_union))
|
||||
{
|
||||
StructUnion su;
|
||||
|
@ -547,7 +560,25 @@ struct Parser
|
|||
}
|
||||
index++;
|
||||
|
||||
model.structUnions.push_back(su);
|
||||
// Handle forward declarations
|
||||
auto found = structUnions.find(su.name);
|
||||
if (found != structUnions.end())
|
||||
{
|
||||
auto& oldSu = model.structUnions[found->second];
|
||||
if (oldSu.size != -1)
|
||||
{
|
||||
errLine(startToken, "cannot redeclare type");
|
||||
return false;
|
||||
}
|
||||
// Replace the forward declared type with the full type
|
||||
oldSu = su;
|
||||
}
|
||||
else
|
||||
{
|
||||
structUnions.emplace(su.name, model.structUnions.size());
|
||||
model.structUnions.push_back(su);
|
||||
}
|
||||
|
||||
if (!isToken(Lexer::tok_semic))
|
||||
{
|
||||
errLine(curToken(), "expected semicolon!");
|
||||
|
@ -559,7 +590,13 @@ struct Parser
|
|||
else if (isToken(Lexer::tok_semic))
|
||||
{
|
||||
// Forward declaration
|
||||
model.structUnions.push_back(su);
|
||||
su.size = -1;
|
||||
auto found = structUnions.find(su.name);
|
||||
if (found == structUnions.end())
|
||||
{
|
||||
structUnions.emplace(su.name, model.structUnions.size());
|
||||
model.structUnions.push_back(su);
|
||||
}
|
||||
eatSemic();
|
||||
return true;
|
||||
}
|
||||
|
@ -577,12 +614,16 @@ struct Parser
|
|||
if (isToken(Lexer::tok_enum))
|
||||
{
|
||||
Enum e;
|
||||
std::string etype;
|
||||
index++;
|
||||
if (isTokenList({ Lexer::tok_identifier, Lexer::tok_bropen }))
|
||||
{
|
||||
e.name = lexer.TokString(curToken());
|
||||
index += 2;
|
||||
|
||||
// TODO: support custom enum types (: type)
|
||||
etype = "int";
|
||||
|
||||
while (!isToken(Lexer::tok_brclose))
|
||||
{
|
||||
if (isToken(Lexer::tok_eof))
|
||||
|
@ -653,7 +694,7 @@ struct Parser
|
|||
}
|
||||
index++; //eat tok_brclose
|
||||
|
||||
model.enums.push_back(e);
|
||||
model.enums.emplace_back(e, etype);
|
||||
if (!isToken(Lexer::tok_semic))
|
||||
{
|
||||
errLine(curToken(), "expected semicolon!");
|
||||
|
@ -740,6 +781,7 @@ struct Parser
|
|||
index++;
|
||||
|
||||
Function fn;
|
||||
fn.typeonly = true;
|
||||
if (!parseFunction(tlist, fn, true))
|
||||
{
|
||||
return false;
|
||||
|
@ -752,9 +794,9 @@ struct Parser
|
|||
}
|
||||
eatSemic();
|
||||
|
||||
// TODO: put the function somewhere
|
||||
model.functions.push_back(fn);
|
||||
|
||||
printf("TODO function pointer: %s\n", fn.name.c_str());
|
||||
// TODO: handle pointer stuff correctly?
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -849,9 +891,7 @@ struct Parser
|
|||
}
|
||||
eatSemic();
|
||||
|
||||
// TODO: put the function somewhere
|
||||
|
||||
printf("TODO function: %s\n", fn.name.c_str());
|
||||
model.functions.push_back(fn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue