[PDB] `findFullDeclForForwardRef` can return incorrect type index in incrementally linked PDBs
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Incrementally linked PDBs can contain more than one tag type (`LF_CLASS`, `LF_STRUCTURE`, `LF_UNION`) with the same unique name and thus the same hash value. When encountering a forward reference to such type, it's not clear which type is referenced. Consider the following example:
1. Create `main.cpp`
```cpp
struct Foo {
// Foo *ptr = nullptr;
int a = 1;
};
int main() {
Foo f;
Foo *p = &f;
return f.a + p->a;
}
```
2. Compile with
```
cl main.cpp /GS- /Z7 /nologo /link /nodefaultlib /entry:main
```
3. Uncomment `Foo *ptr = nullptr;`
4. Recompile
When we dump the types (TPI) from generated PDB, we get
- 0x1000: LF_ARGLIST (...)
- 0x1001: LF_PROCEDURE (...)
- 0x1002: LF_STRUCTURE name: `Foo`, unique name: `.?AUFoo@@`, options: forward ref | has unique name
- 0x1003: LF_POINTER referent: 0x1002
- 0x1004: LF_MFUNCTION (...)
- 0x1005: LF_FIELDLIST { a, Foo }
- 0x1006: LF_STRUCTURE name: `Foo`, unique name: `.?AUFoo@@`, field list: 0x1005, options: has ctor / dtor | has unique name
- 0x1007: LF_FIELDLIST { ptr, a, Foo }
- 0x1008: LF_STRUCTURE name: `Foo`, unique name: `.?AUFoo@@`, field list: 0x1007, options: has ctor / dtor | has unique name
**Which type is 0x1002 referring to?**
From the program, we know that it must refer to 0x1008, because that type has the `ptr` member.
But how could we know this from the PDB?
Currently, [`TpiStream::findFullDeclForForwardRef`](https://github.com/llvm/llvm-project/blob/f146677396ea0b02b902743633a1f934023ae15f/llvm/lib/DebugInfo/PDB/Native/TpiStream.cpp#L178-L218) hashes the unique name and looks in the hash table for a non-forward-reference. This is used to annotate forward references in `llvm-pdbutil dump`.
However, in our case, we will find two entries. It picks the first, which is incorrect.
While it's correct here, always picking the last entry isn't a solution either. If we comment out `Foo *ptr = nullptr;` again and recompile, we will find that the records in the TPI stream haven't changed. So now, picking the first entry would be correct.
LLDB's native PDB reader uses `findFullDeclForForwardRef` as well, so it will sometimes show the wrong type.
I'm not sure what the correct approach here is. Both MS DIA and the VS debugger resolve the correct type.
Contributor guide
Assessment
This issue has not been assessed yet.