-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Fix unnecessary failure when Crossgen'ing dll's without proper Debug Directory data #73824
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
Conversation
…d by Crossgen2. Instead of failing unnecessarily, it now emits a warning.
| } | ||
| catch (BadImageFormatException e) | ||
| { | ||
| if (e.Message.Equals("Invalid directory size.")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
guess this can be factored out to avoid duplication?
| // This comes from the Size property of the DebugDirectoryEntry class. | ||
| int expectedDbgDirSizeBase = 28; | ||
|
|
||
| Console.WriteLine("WARNING: This image's Debug Directory Entry size should be a" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these can be created as exception strings instead of string literals?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree we should make this an exception string. I would throw away the "artificial intelligence" analyzing the exception message and just print something generic like "WARNING: %s contains invalid debug information: %s" with the name of the file and the message of the exception caught.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can afford to keep the part that says "Debugging tools may not work correctly with this image". I feel our users would like to know what the warning entails for them.
| } | ||
| catch (BadImageFormatException e) | ||
| { | ||
| if (e.Message.Equals("Invalid directory size.")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure if this should be special cased, since on non-english locales this would probably fail anyways
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I special cased this as well because it failed otherwise with the same BadImageFormatException.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since e.Message will be localized, this check will not work for any language except English.
trylek
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly looks good, thanks Ivan! I concur with Manish that we should somewhat simplify the warning logic, I have shared my thoughts on that in the PR comments.
|
Could we create a helper method like below and use it instead of public static ImmutableArray<DebugDirectoryEntry> ReadDebugDirectoryIgnoringErrors(this PEReader moduleReader)
{
try
{
return moduleReader.ReadDebugDirectory();
}
catch (BadImageFormatException e)
{
// Report a warning using e.Message
return ImmutableArray<DebugDirectoryEntry>.Empty;
}
} |
|
Thank you all for the feedback! I have a few questions whatsoever. First, I'm a big advocate of helper functions as long as it doesn't end up being a big scramble of chained functions (which this is clearly not the case). My question I would like guidance on is where would be the most appropriate place/file to write the helper function, since these two files are in different csproj's. One is in My other question is regarding the string literal, and it's in a very similar boat. The original throwing function in Lines 541 to 544 in 10438a5
This runtime/src/libraries/System.Reflection.Metadata/src/Resources/Strings.resx Lines 270 to 272 in d34c0ff
I might have missed something, but I couldn't get any sort of access to there from our codebase. So, we might just have to create a constant like you mentioned, but my question is where would be most appropriate. |
|
Would it be possible to add both the helper and the resource string to a shared assembly, e.g., |
|
@ivdiazsa - what you're looking at is standardized support for localized resources. Crossgen2 uses them too, e.g. here:
This is coming from the file
that you can edit with VS (or manually). Having said that, proper location depends on the assembly you put the helper in. Are you sure that you see a failure in |
It would be nice to have the same behavior for both. And there are other places that should be fixed, e.g., Line 253 in ad60e44
|
|
If we want a common fix for Crossgen2 and NativeAOT, it should be probably under https://github.com/dotnet/runtime/blob/main/src/coreclr/tools/Common/System/FormattingHelpers.cs |
|
|
||
| Console.WriteLine("WARNING: This image's Debug Directory Entry size should be a" | ||
| + $" multiple of {expectedDbgDirSizeBase}, but was {actualDbgDirSize}." | ||
| + " Debugging tools may not work correctly with this image."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| + " Debugging tools may not work correctly with this image."); | |
| + " Diagnostic tools may not work correctly with this image."); |
It is also profilers, etc.
| } | ||
| catch (BadImageFormatException e) | ||
| { | ||
| if (e.Message.Equals("Invalid directory size.")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are many other ways that the debug symbols can be corrupted, we just have not seen them. Should we catch all exceptions here and print that the debug symbols are corrupted, and include the error message from the exception?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are many other ways that the debug symbols can be corrupted, we just have not seen them. Should we catch all exceptions here and print that the debug symbols are corrupted, and include the error message from the exception?
None of the compiler is hardened for obfuscators. I wouldn't treat debug information very differently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(We might successfully open the debug information but there can be exceptions once we start looking into it.)
| try | ||
| { | ||
| pdbReader = PortablePdbSymbolReader.TryOpenEmbedded(peReader, GetMetadataStringDecoder()) | ||
| ?? OpenAssociatedSymbolFile(filePath, peReader); | ||
| } | ||
| catch (BadImageFormatException e) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is placed in a very broad spot. A lot of code is going to run and could throw a BadImageFormat (S.R.Metadata will throw it for any sort of corruption).
Can you instead add a e.g. SafeReadDebugDirectory(this PEReader peReader) extension method that simply checks the directory size before calling the API and returns ImmutableArray<DebugDirectoryEntry>.Empty if it doesn't make sense?
But doing Console.WriteLine in arbitrary spots is not a great practice because we're multithreaded. I would honestly just swallow this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did not consider the fact of multithreading. Thanks for your feedback Michal! I think this is a great suggestion, I'll get to work on it.
…ng with exceptions and regional issues.
|
Thank you, that is much cleaner. I think we should also fix one more place I mentioned above: Line 253 in ad60e44
There are also two occurrences of |
| int actualDbgDirSize = peReader.PEHeaders.PEHeader.DebugTableDirectory.Size; | ||
|
|
||
| // This comes from the Size property of the DebugDirectoryEntry class. | ||
| int expectedDbgDirSizeBase = 28; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Might use const here:
| int expectedDbgDirSizeBase = 28; | |
| const int ExpectedDbgDirSize = 28; |
Sure thing. I wasn't sure whether I should be meddling with PGO as well, but you're right, it doesn't seem like it would break anything in this case. |
|
It would be nice to have consistent behavior at least within our own AoT toolchain. |
AntonLapounov
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for fixing this!
Fixes #72425. When trying to use Crossgen2 with dll's that do not have the proper debug directory data (e.g. obfuscated dll's), a
BadImageExceptionerror is thrown and thus failing the process. In this case, the dll is still usable though debugging tools will probably not work. So, instead of failing, we ought to let the user continue but we shall warn them about this. As a history tidbit, this was the default behavior in .NET 5.