Skip to content
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

"Expected a null constant." error when saving assembly with pdb #550

Open
maxkatz6 opened this issue Mar 25, 2024 · 7 comments
Open

"Expected a null constant." error when saving assembly with pdb #550

maxkatz6 opened this issue Mar 25, 2024 · 7 comments

Comments

@maxkatz6
Copy link

I noticed this issue only happens when I used "local const" in the code, specifically when const type is an enum from another assembly.
For example, this method:

public static bool IsMatch(string text, string pattern, CompareInfo compareInfo)
{
    const CompareOptions options = CompareOptions.IgnoreSymbols | CompareOptions.StringSort | CompareOptions.IgnoreNonSpace;
    return compareInfo.Compare(text, pattern, options) == 0;
}

Simply replacing const CompareOptions options with CompareOptions options will fix the problem, so we have a workaround, though this bug is confusing.
Repro project (both dll project and executable console app):

ClassLibrary1.zip

@maxkatz6
Copy link
Author

Also, unrelated to the original issue, and could be my misunderstanding, but I can't really run this code:

var filePath = args[0];
var asm = AssemblyDef.Load(filePath);
...
asm.Write(filePath, writerParameters);

as it will fail with System.IO.IOException: The requested operation cannot be performed on a file with a user-mapped section open.. Does dnLib hold file open after loading while trying to write to the same file?

@wtfsck
Copy link
Contributor

wtfsck commented Mar 28, 2024

It's memory mapped and you're using Windows which doesn't allow writing to the file until it's closed. You could instead read all bytes from the file and pass in the byte[] to the Load() method to load it that way and nothing gets memory mapped.

@wtfsck
Copy link
Contributor

wtfsck commented Mar 28, 2024

I think you need to add an assembly resolver to fix this. See the README on how to do that.

@maxkatz6
Copy link
Author

It's memory mapped and you're using Windows which doesn't allow writing to the file until it's closed. You could instead read all bytes from the file and pass in the byte[] to the Load() method to load it that way and nothing gets memory mapped.

Yeah, reading from byte array works fine. That's what I also did in the sample.

I think you need to add an assembly resolver to fix this. See the README on how to do that.

Hm. In my actual project I did use an assembly resolver, where I initially found this issue. I trimmed everything down until I got this minimal sample.
I can try to update the repro of the issue, but it still fails with resolver. And removing const magically solves the issue.

@wtfsck
Copy link
Contributor

wtfsck commented Mar 29, 2024

Make sure the assembly resolver finds the assembly with the enum CompareOptions and any other assemblies it references, if any.

@UlyssesWu
Copy link

UlyssesWu commented Jun 10, 2024

I met this issue, too. Could we just skip this error? Sometimes we cannot find every dll reference, but we still want some pdb info kept. However with this issue we can only get an empty pdb.

I have written some simple code to bypass this but I don't know if it will cause more problems, like making this pdb unusable.

case ElementType.Class:
WriteTypeDefOrRef(writer, ((ClassSig)type).TypeDefOrRef);
if (value is byte[])
writer.WriteBytes((byte[])value);
else if (value is not null)
helper.Error("Expected a null constant");
return;

    case ElementType.Class when value is not byte[] && value is not null:
	    var realElementType = ConstantUser.GetElementType(value);
	    if (realElementType != ElementType.End) {
		    writer.WriteByte((byte)realElementType);
		    WritePrimitiveValue(writer, realElementType, value);
		    WriteTypeDefOrRef(writer, type.ToTypeDefOrRef());
		    return;
	    }
	    break;

@philipp-naused
Copy link

philipp-naused commented Oct 1, 2024

As a workaround, you can remove the "invalid" constants from the PDB:

foreach (MethodDef method in new MemberFinder().FindAll(module).MethodDefs.Keys)
{
    IList<PdbConstant>? constants = method.Body?.PdbMethod?.Scope?.Constants;
    if (constants is not null)
    {
        for (int i = constants.Count - 1; i >= 0; i--)
        {
            // workaround for https://github.com/0xd4d/dnlib/issues/550
            // remove any constant with a reference type where the value is not a byte[] or null.
            PdbConstant constant = constants[i];
            if (constant.Type?.ElementType is ElementType.Class &&
                constant.Value is not null and not byte[])
            {
                constants.RemoveAt(i);
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants