I’m working on a project in C++ on Windows ARM64, and I’ve run into an issue when trying to retrieve function boundaries using RtlLookupFunctionEntry.
Specifically:
- The BeginAddress returned is always correct.
- However, the calculated function size (runtimeFunction->FunctionLength) seems to overshoot by hundreds of bytes.
- This only happens on ARM64, the same logic works fine on x64.
- Sometimes (depending on when I rebuild code or not) the FunctionLength is correct but if I rebuild the project it breaks again and overshoots, but checking a function ntdll.dll for example always returns a FunctionSize that is way to big.
For reference, I’m calling RtlLookupFunctionEntry like this:
PRUNTIME_FUNCTION pRuntimeFunction = RtlLookupFunctionEntry(RIP, &ImageBase, nullptr);
Here is the PRUNTIME_FUNCTION struct for ARM64:
typedef struct _IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY {
DWORD BeginAddress;
union {
DWORD UnwindData;
struct {
DWORD Flag : 2;
DWORD FunctionLength : 11;
DWORD RegF : 3;
DWORD RegI : 4;
DWORD H : 1;
DWORD CR : 2;
DWORD FrameSize : 9;
} DUMMYSTRUCTNAME;
} DUMMYUNIONNAME;
} IMAGE_ARM64_RUNTIME_FUNCTION_ENTRY, * PIMAGE_ARM64_RUNTIME_FUNCTION_ENTRY;
Here is some example code that overshoots:
ULONG_PTR imageBase = 0;
PRUNTIME_FUNCTION fn = RtlLookupFunctionEntry((ULONG_PTR)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtClose"), &imageBase, NULL); //NtClose is only 8 bytes long -> svc 0x0F, ret;
if (fn != NULL)
{
printf("Begin Address: 0x%p\n", imageBase + fn->BeginAddress);
printf("Size: %d\n", fn->FunctionLength); //ARM64 instructions are always 4 bytes, so true length is "(fn->FunctionLength * 4)", but it returns something like 1000+ for some reason?
}
else
{
printf("fn was NULL!\n");
}
Some extra context:
- I’m working in usermode (not kernel).
- The modules are standard Windows binaries (no custom-built ones).
- I’ve verified that the function’s unwind info looks valid.
- Manually disassembling confirms the function is smaller than what Rtl reports.
My Questions:
- Is this a known quirk on ARM64 Windows, or could I be missing something about how ARM64’s unwind information or function tables work?
Any help or insight would be hugely appreciated, been banging my head against this for a bit now.