Idaclang does not correctly handle calling convention declarations in class member functions


extern "C++"
{
	struct IUnknown333
	{
	public:
		virtual int __stdcall QueryInterface(const void* riid, void** ppvObject) = 0;

		virtual int __stdcall AddRef(void) = 0;

		virtual int __stdcall Release(void) = 0;
	};
}

int __fastcall FindWindow333(int a1, double a2);


This problem also exists in idaclang.exe. The following code is a dump of TIL generated by idaclang.exe

struct __cppobj IUnknown333 {IUnknown333_vtbl *__vftable /*VFT*/;};
struct /*VFT*/ IUnknown333_vtbl {int (__cdecl *QueryInterface)(IUnknown333 *__hidden this, const void *riid, void **ppvObject);int (__cdecl *AddRef)(IUnknown333 *__hidden this);int (__cdecl *Release)(IUnknown333 *__hidden this);};

int __cdecl ?AddRef_IUnknown333__UAGHXZ(IUnknown333 *__hidden this);
int __fastcall ?FindWindow333__YIHHN_Z(int a1, double a2);
int __cdecl ?QueryInterface_IUnknown333__UAGHPBXPAPAX_Z(IUnknown333 *__hidden this, const void *riid, void **ppvObject);
int __cdecl ?Release_IUnknown333__UAGHXZ(IUnknown333 *__hidden this);

Hello,

Indeed, it seems clang parser behaves incorrectly here. Please use the built-in parser for COM classes.

But, I need use idaclang for build a new til from many complex header files such as those using c++17 syntax, It cannot be done via the built-in parser.

As a workaround, could you try to specify stdcall as the default calling convention?

I know what you said can be achieved through Ida’s built-in idaclang.dll plug-in, but it seems that it does not support the creation of an independent til, so I need to use an independent idaclang.exe to achieve this purpose, but I don’t know how to specify __stdcall as the default calling convention in idaclang.exe.

Indeed, my advice is not working, sorry

Thank you very much. Can you tell me if this problem will be fixed in the next version? Or it would be better if a version that temporarily fixes this problem can be released in advance.

Finally, after a break during the May Day holiday, I completed the patch repair work on idaclang.exe by myself.
idaclang_fix.7z

// VCRUNTIME140_1_for_idaclang.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"

#include <pro.h>
#include <idp.hpp>
#include <idd.hpp>
#include <typeinf.hpp>
#include "../ldr/pe/pe.h"

#include <clang-c/Index.h>

#include "Hook.h"


BEGIN_NAMESPACE_EX(UCode)

CAtlTypedPtrList<CXCursor*> t_lst_pcursor;

CAtlTypedPtrList<CXCursorVisitor> t_lst_Real_visitor;
enum CXChildVisitResult My_CXCursorVisitor(CXCursor cursor, CXCursor parent, CXClientData client_data)
{
	t_lst_pcursor.AddTail(&cursor);

	CXCursorVisitor Real_visitor = t_lst_Real_visitor.GetTail();
	CXChildVisitResult ret = Real_visitor(cursor, parent, client_data);

	t_lst_pcursor.RemoveTail();
	return ret;
}

unsigned (*Real_clang_visitChildren)(CXCursor parent, CXCursorVisitor visitor, CXClientData client_data);
unsigned My_clang_visitChildren(CXCursor parent, CXCursorVisitor visitor, CXClientData client_data)
{
	CXCursorVisitor visitor_bak = visitor;
	t_lst_Real_visitor.AddTail(visitor);
	visitor = My_CXCursorVisitor;

	unsigned ret = Real_clang_visitChildren(parent, visitor, client_data);

	t_lst_Real_visitor.RemoveTail();
	return ret;
}

__int64(__fastcall* Real_visitVTableComponents_Callback)(int type, CXCursor* pcursor, __int64* a3);
__int64 __fastcall My_visitVTableComponents_Callback(int type, CXCursor* pcursor, __int64* a3)
{
	t_lst_pcursor.AddTail(pcursor);

	__int64 ret = Real_visitVTableComponents_Callback(type, pcursor, a3);

	t_lst_pcursor.RemoveTail();
	return ret;
}

bool (ida_export* Real_create_tinfo)(tinfo_t* _this, type_t bt, type_t bt2, void* ptr);
bool ida_export My_create_tinfo(tinfo_t* _this, type_t bt, type_t bt2, void* ptr)
{
	if (bt == BT_FUNC && bt2 == BT_FUNC)
	{
		func_type_data_t* p = (func_type_data_t*)ptr;

		CXCursor& cursor = *t_lst_pcursor.GetTail();
		//CXCursorKind kind = clang_getCursorKind(cursor);
		CXType funcType = clang_getCursorType(cursor);

		CXCallingConv callingConv = CXCallingConv_Invalid;
		if (funcType.kind == CXType_Typedef)
		{
			// 获取 typedef 的基础类型
			CXType underlyingType = clang_getTypedefDeclUnderlyingType(cursor);
			CXTypeKind underlyingTypeKind = underlyingType.kind;

			// 检查基础类型是否是函数原型类型 (FunctionProto) 或函数非原型类型 (FunctionNoProto)
			// 或者是指向这些函数类型的指针类型
			CXType typeToInspect = underlyingType;

			if (underlyingTypeKind == CXType_Pointer)
			{
				// 如果是函数指针,我们需要获取它指向的类型
				CXType pointeeType = clang_getPointeeType(underlyingType);
				if (pointeeType.kind == CXType_FunctionProto || pointeeType.kind == CXType_FunctionNoProto)
				{
					typeToInspect = pointeeType;
				}
			}

			if (typeToInspect.kind == CXType_FunctionProto || typeToInspect.kind == CXType_FunctionNoProto)
			{
				callingConv = clang_getFunctionTypeCallingConv(typeToInspect);
			}
		}
		else
		{
			callingConv = clang_getFunctionTypeCallingConv(funcType);
		}
		if (callingConv != CXCallingConv_Invalid)
		{
			switch (callingConv)
			{
			case CXCallingConv_X86StdCall:
				p->cc = CM_CC_STDCALL;
				break;
			case CXCallingConv_C:
				p->cc = CM_CC_CDECL;
				break;
			case CXCallingConv_X86ThisCall:
				p->cc = CM_CC_THISCALL;
				break;
			case CXCallingConv_X86FastCall:
				p->cc = CM_CC_FASTCALL;
				break;
			case CXCallingConv_X86Pascal:
				p->cc = CM_CC_PASCAL;
				break;
			default:
				ATLASSERT(FALSE);
				break;
			}
		}
		else
		{
			ATLASSERT(callingConv != CXCallingConv_Invalid);
		}
	}
	bool result = Real_create_tinfo(_this, bt, bt2, ptr);
	return result;
}

void StartMyWork()
{

	//MessageBox(NULL, "Attach me?", "提示", MB_ICONWARNING);

	TCHAR szAppName[MAX_PATH];
	GetAppFileName(NULL, szAppName, _countof(szAppName));

	if (_tcsicmp(szAppName, _T("idaclang.exe")))
	{
		return;
	}

	HMODULE hmod_libclang_dll = LoadLibrary(_T("..\\..\\libclang.dll"));
	if (hmod_libclang_dll)
	{
		unsigned (*pfn_clang_visitChildren)(CXCursor parent, CXCursorVisitor visitor, CXClientData client_data) = (unsigned int(__cdecl*)(CXCursor, CXCursorVisitor, CXClientData))GetProcAddress(hmod_libclang_dll, "clang_visitChildren");
		if (pfn_clang_visitChildren)
		{
			_HookFunction_With_CommitImmediately(pfn_clang_visitChildren, My_clang_visitChildren, (void**)&Real_clang_visitChildren);
		}
	}

	HMODULE hmod_idaclang_exe = GetModuleHandle(_T("idaclang.exe"));

	{
		PBYTE pcbCode = 0x00007FF68B4F3350 - 0x7FF68B4F0000 + (PBYTE)hmod_idaclang_exe;
		_HookFunction_With_CommitImmediately(pcbCode, My_visitVTableComponents_Callback, (void**)&Real_visitVTableComponents_Callback);
	}

	{
		PBYTE pcbCode = 0x00007FF68B52DAD0 - 0x7FF68B4F0000 + (PBYTE)hmod_idaclang_exe;
		_HookFunction_With_CommitImmediately(pcbCode, My_create_tinfo, (void**)&Real_create_tinfo);
	}
}

void EndMyWork()
{
	TCHAR szAppName[MAX_PATH];
	GetAppFileName(NULL, szAppName, _countof(szAppName));

	if (_tcsicmp(szAppName, _T("idaclang.exe")))
	{
		return;
	}

	HMODULE hmod_idaclang_exe = GetModuleHandle(_T("idaclang.exe"));

	if (Real_create_tinfo)
	{
		PBYTE pcbCode = 0x00007FF68B52DAD0 - 0x7FF68B4F0000 + (PBYTE)hmod_idaclang_exe;
		_UnhookFunction_With_CommitImmediately(pcbCode, My_create_tinfo, (void**)&Real_create_tinfo);
	}

	if (Real_visitVTableComponents_Callback)
	{
		PBYTE pcbCode = 0x00007FF68B4F3350 - 0x7FF68B4F0000 + (PBYTE)hmod_idaclang_exe;
		_UnhookFunction_With_CommitImmediately(pcbCode, My_visitVTableComponents_Callback, (void**)&Real_visitVTableComponents_Callback);
	}

	if (Real_clang_visitChildren)
	{
		HMODULE hmod_libclang_dll = GetModuleHandle(_T("..\\..\\libclang.dll"));
		if (hmod_libclang_dll)
		{
			unsigned (*pfn_clang_visitChildren)(CXCursor parent, CXCursorVisitor visitor, CXClientData client_data) = (unsigned int(__cdecl*)(CXCursor, CXCursorVisitor, CXClientData))GetProcAddress(hmod_libclang_dll, "clang_visitChildren");
			if (pfn_clang_visitChildren)
			{
				_UnhookFunction_With_CommitImmediately(pfn_clang_visitChildren, My_clang_visitChildren, (void**)&Real_clang_visitChildren);
			}
		}
	}
}

END_NAMESPACE_EX

Yes, the problem will be fixed in the next release.
Thank you for publishing your workaround!

1 Like