Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Class fields missing in NativeAOT-generated PDBs (not visible in IDA or other debuggers) #115283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
dadavadd opened this issue May 4, 2025 · 6 comments
Labels
area-NativeAOT-coreclr untriaged New issue has not been triaged by the area owner

Comments

@dadavadd
Copy link
Contributor

dadavadd commented May 4, 2025

Hello.

I'm encountering an issue with the PDBs generated by NativeAOT: while they correctly list all fields of user-defined classes, they often report an incorrect total type size. This inconsistency makes it difficult to analyze memory layout using reverse engineering or debugging tools such as IDA Pro, since such tools rely on accurate type sizes to determine field boundaries and class structure.

Repro scenario
In my project, I have a class like this:

public class WindowsClipboardMonitor
{
    private readonly PCWSTR _className;
    private readonly HWND _hwnd;
    private readonly Thread _messageLoopThread;
    private readonly uint _messageLoopThreadId;
    private readonly bool _isDisposed;
    private readonly CancellationToken _token;
    private readonly Func<string, Task> ClipboardUpdate;
}

After compiling the app with NativeAOT and inspecting the resulting PDB using IDA Pro, I observe the following:

Type: ClipboardTranslator_Core_ClipboardTranslator_Core_ClipboardHandler_WindowsClipboardMonitor | Index: 19481 | Size: 8
Field: Object
Field: +0020: ClipboardTranslator_Core_Windows_Win32_Foundation_PCWSTR _className
Field: +0028: ClipboardTranslator_Core_Windows_Win32_Foundation_HWND _hwnd
Field: +0008: (S_P_CoreLib_System_Threading_Thread)* _messageLoopThread
Field: +0018: UInt32 _messageLoopThreadId
Field: +001C: Boolean8 _isDisposed
Field: +0030: S_P_CoreLib_System_Threading_CancellationToken _token
Field: +0010: (S_P_CoreLib_System_Func_2<String__S_P_CoreLib_System_Threading_Tasks_Task>)* ClipboardUpdate

From this layout, it's clear the actual type size should be at least 0x38 (56 bytes), not 8. But NativeAOT reports the size as 8.

Image

To confirm that the issue is only with the reported size, I manually patched the corresponding byte in the .pdb file (or in-memory via IDA) to change the reported type size from 0x08 to 0x38.

Image

Image

After doing this, IDA Pro immediately recognizes the full layout, and all fields are displayed correctly, including those beyond the 8-byte boundary.

Image

This confirms that:

  • The field list and their offsets are correct in the PDB.
  • The only incorrect part is the type's reported size.
  • Tools depending on type size use that value to stop parsing fields.

This issue does not affect value types (structs). All struct sizes in the same PDB are reported correctly, and their fields are fully visible in IDA without patching.

@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label May 4, 2025
Copy link
Contributor

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

@dadavadd
Copy link
Contributor Author

dadavadd commented May 4, 2025

Image

@dadavadd
Copy link
Contributor Author

dadavadd commented May 4, 2025

The problem may be writing the size of the class (LF_CLASS) in the file. CodeViewTypesBuilder.cs Unfortunately, I spent the whole day working on the problem in disassembler and couldn't solve it.

@SingleAccretion
Copy link
Contributor

The problem may be writing the size of the class (LF_CLASS) in the file.

Indeed. To be more precise, here's where 8 (the pointer size) comes from:

LayoutInt elementSize = defType.GetElementSize();
int elementSizeEmit = elementSize.IsIndeterminate ? 0xBAAD : elementSize.AsInt;
ClassFieldsTypeDescriptor fieldsDescriptor = new ClassFieldsTypeDescriptor
{
Size = (ulong)elementSizeEmit,
FieldsCount = fieldsDescs.Count,

Reading LLVM source, I think we're supposed to put ClassTypeDescriptor.Size into the record (i. e. the full size including all base classes) and ClassFieldsTypeDescriptor.Size is redundant. But this would need to be confirmed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-NativeAOT-coreclr untriaged New issue has not been triaged by the area owner
Projects
Status: No status
Development

No branches or pull requests

2 participants