This is an archive of the discontinued LLVM Phabricator instance.

[PDB] Fix emission of PDB string table
ClosedPublic

Authored by zturner on Feb 14 2018, 4:41 PM.

Details

Summary

This was originally reported as a bug with the symptom being "cvdump crashes when printing an LLD-linked PDB that has an S_FILESTATIC record in it". After some additional investigation, I determined that this was a symptom of a larger problem, and in fact the real problem was in the way we emitted the global PDB string table. As evidence of this, you can take any lld-generated PDB, run cvdump -stringtable on it, and it would return no results.

My hypothesis was that cvdump could not *find* the string table to begin with. Normally it would do this by looking in the "named stream map", finding the string /names, and using its value as the stream index. If this lookup fails, then cvdump would fail to load the string table.

To test this hypothesis, I looked at the name stream map generated by a link.exe PDB, and I emitted exactly those bytes into an LLD-generated PDB. Suddenly, cvdump could read our string table!

This code has always been hacky and we knew there was something we didn't understand. After all, there were some comments to the effect of "we have to emit strings in a specific order, otherwise things don't work". The key to fixing this was finally understanding this.

The way it works is that it makes use of a generic serializable hash map that maps integers to other integers. In this case, the "key" is the offset into a buffer, and the value is the stream number. If you index into the buffer at the offset specified by a given key, you find the name. The underlying cause of all these problems is that we were using the identity function for the hash. i.e. if a string's offset in the buffer was 12, the hash value was 12. Instead, we need to hash the string *at that offset*. There is an additional catch, in that we have to compute the hash as a uint32 and then truncate it to uint16.

Making this work is a little bit annoying, because we use the same hash table in other places as well, and normally just using the identity function for the hash function is actually what's desired. I'm not totally happy with the template goo I came up with, but it works in any case.

The reason we never found this bug through our own testing is because we were building a /parallel/ hash table (in the form of an llvm::StringMap<>) and doing all of our lookups and "real" hash table work against that. I deleted all of that code and now everything goes through the real hash table. Then, to test it, I added a unit test which adds 7 strings and queries the associated values. I test every possible insertion order permutation of these 7 strings, to verify that it really does work as expected.

Diff Detail

Repository
rL LLVM

Event Timeline

zturner created this revision.Feb 14 2018, 4:41 PM

Excellent patch description! It's reassuring to see all the nagging questions explained. I like the test, too.

llvm/include/llvm/DebugInfo/PDB/Native/HashTable.h
180 ↗(On Diff #134338)

The usual idiom is:

using std::swap;
swap(Present, NewMap.Present);

That makes it possible for ADL to find if there's a specialization for swap and to fall back to std::swap if there isn't. It doesn't matter much here, since SparseBitVector doesn't seem to have a swap specialization.

llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp
35 ↗(On Diff #134338)

This sentence ("Here, ... unsigned short.") seems unnecessary. The type is actually std::uint16_t, and it's just above. Also, it detracts from the important comment that follows.

zturner added inline comments.Feb 15 2018, 11:39 AM
llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp
35 ↗(On Diff #134338)

It's uint16_t in my code, but it's only correct if it's also uint16 in the reference implementation. But the signature in the reference implementation uses the type HASH for the return value, and it can be easily overlooked that that type is in fact also a uint16. That was the purpose of the comment here.

Anyone have any comments on this?

amccarth accepted this revision.Feb 16 2018, 11:36 AM

LGTM.

llvm/lib/DebugInfo/PDB/Native/NamedStreamMap.cpp
35 ↗(On Diff #134338)

Got it.

This revision is now accepted and ready to land.Feb 16 2018, 11:36 AM
rnk accepted this revision.Feb 16 2018, 11:51 AM

Ship it! =D

This revision was automatically updated to reflect the committed changes.