Как диагностировать сбой загрузки типа из PEVerify

Я работаю над компилятором, который выдает плохой результат в некоторых случаях расширения генераторов. PEVerify просто говорит «Ошибка загрузки типа», не объясняя, почему. Когда я видел это в прошлом, обычно это было потому, что сгенерированный тип имеет неправильное количество общих параметров, но здесь, кажется, все совпадает.

Есть ли хороший способ получить более подробную информацию о том, что пошло не так с сгенерированным типом? За исключением этого, есть ли какие-нибудь хорошие советы и методы, как отследить ошибку?

Вывод из PEVerify:

C:\Build\Test> проверить testcase.exe /VERBOSE /UNIQUE

Microsoft (R) .NET Framework PE Verifier. Версия 4.0.30319.0 Copyright (c) Microsoft Corporation. Все права защищены.

[IL]: ошибка: [C:\Build\Test\testcase.exe : Testing.Linq_operatorModule::IndexWhereImpl[T]][mdToken=0x6000002][смещение 0x00000002] Не удалось разрешить токен.

[IL]: Ошибка: [C:\Build\Test\testcase.exe : Testing.Linq_operatorModule+$IndexWhereImpl$3`1[T]::.ctor][mdToken=0x6000006] [HRESULT 0 x8007000B] — была предпринята попытка загрузить программу с неправильным форматом.

[токен 0x02000004] Ошибка загрузки типа.

3 Ошибки при проверке testcase.exe

Полный дамп от ILDasm находится здесь, так как он слишком велик для размещения в сообщении SO.


person Mason Wheeler    schedule 01.01.2017    source источник
comment
В вашем случае это не причина, но у меня загрузка типа не удалась, потому что я забыл добавить модификатор новостного слота в метод.   -  person Petr Hudeček    schedule 24.09.2019


Ответы (1)


Что-то не так с привязкой параметров вашего типа во всем, что генерирует этот код. Результирующий IL может быть собран, но он настолько недействителен, что PEVerify полностью задыхается от него (что, возможно, является ошибкой в ​​​​PEVerify, но такие вещи, как Mono.Cecil, тоже не нравятся этому коду).

Например:

  .method /*06000002*/ private hidebysig static 
          class [mscorlib/*23000004*/]System.Collections.Generic.IEnumerable`1/*01000003*/<int32> 
          IndexWhereImpl<T>(class [mscorlib/*23000004*/]System.Collections.Generic.IEnumerable`1/*01000003*/<!!T> coll,
                            class [mscorlib/*23000004*/]System.Func`2/*01000004*/<!!T,bool> 'filter') cil managed
  {
    // Code size       8 (0x8)
    .maxstack  8
    IL_0000:  ldarg.0
    IL_0001:  ldarg.1
    IL_0002:  newobj     instance void Testing.Linq_operatorModule/*02000002*//$IndexWhereImpl$3`1/*02000003*/::.ctor(class [mscorlib/*23000004*/]System.Collections.Generic.IEnumerable`1/*01000003*/<!!T>,
                                                                                                                      class [mscorlib/*23000004*/]System.Func`2/*01000004*/<!!T,bool>) /* 06000006 */
    IL_0007:  ret
  } // end of method Linq_operatorModule::IndexWhereImpl

Вызов дизассемблированного конструктора недопустим; правильный вызов будет

newobj instance void class Testing.Linq_operatorModule/$IndexWhereImpl$3`1<!!T>::.ctor(
    class [mscorlib]System.Collections.Generic.IEnumerable`1<!0>,
    class [mscorlib]System.Func`2<!0,bool>
)

Исходный вызов был бы уместен для универсального метода, но мы вызываем не универсальный метод, а метод экземпляра универсального класса.

Оставшийся код выглядит следующим образом, включая поля с недопустимыми ссылками на параметры:

.field assembly !!0 $value$5

!!0 ссылается на первый параметр типа универсального метода, и вы не можете объявлять поля в методах, так что это всегда неправильно. Он собирается в 0x1e 0x00 (ELEMENT_TYPE_MVAR 0), вы хотели 0x13 0x00 (ELEMENT_TYPE_VAR 0), что соответствует

.field assembly !0 $value$5

Немного удивительно, что ilasm и ildasm даже позволяют это, я ожидаю, что они будут немного более проницательными. Судя по всему, то же самое сделали и автор(ы) PEVerify, хотя это не оправдание.

Я не уверен, как вы генерируете такой код; это может быть ошибка в одном месте, из-за которой все остальное также становится недействительным.

person Jeroen Mostert    schedule 02.01.2017
comment
Я генерирую подобный код с помощью CCI, который с радостью создаст полностью, смехотворно недействительный IL... но, в отличие от Reflection.Emit, он на самом деле создаст его, а не захлебнется проблемами с типом и даст мне черный -box ошибка без возможности запустить ILDasm и PEVerify, чтобы выяснить, что происходит не так. - person Mason Wheeler; 02.01.2017
comment
Так что спасибо. Я рассмотрю привязки универсального типа. Это действительно имеет смысл. Компилятор преобразует универсальный метод в генератор и поднимает локальные методы в поля класса генератора, что означает, что они по-прежнему идентифицируются как использующие параметры универсального типа из исходного метода. Время работать над исправлением этого. - person Mason Wheeler; 02.01.2017