Разрядность .NET сборок
Dec 18, 2007 · Comments.NETИнструментыПрограммированиеWindows
Вскоре после написания поста про определение разрядности исполняемых файлов выяснилось, что в мире .NET «всё совсем по-другому».
Для начала небольшое лирическое отступление. Компилятор Visual C++ использует ключ /clr для указания разрешенных типов кода в .NET сборке:
-
/clr - позволяет смешивать управляемый и неуправляемый код;
-
/clr:pure - указывает, что сборка будет содержать только управляемый код;
-
/clr:safe - идет ещё дальше, чем /clr:pure, разрешая только проверяемый код.
С точки зрения разрядности получаемых модулей тут важно то, что управляемый код по определению может выполняться на любой платформе (x86, amd64 и ia64), в то время как неуправляемый код предназначен для конкретного процессора. Т.е. можно бы было ожидать, что модули, собранные с /clr:pure или /clr:safe, можно использовать на любой платформе. На самом деле это не совсем так, о чём я и расскажу ниже.
Далее. Модули, содержащие управляемый код, можно отличить от «нормальных» модулей по непустой «COM Descriptor Directory». Команда “link /dump /headers” показывает её в конце списка директорий в «PE Optional Header»:
OPTIONAL HEADER VALUES
10B magic # (PE32)
8.00 linker version
...
0 loader flags
10 number of directories
0 [ 0] RVA [size] of Export Directory
87C4 [ 64] RVA [size] of Import Directory
A000 [ 6A8] RVA [size] of Resource Directory
0 [ 0] RVA [size] of Exception Directory
0 [ 0] RVA [size] of Certificates Directory
B000 [ 190] RVA [size] of Base Relocation Directory
3100 [ 1C] RVA [size] of Debug Directory
0 [ 0] RVA [size] of Architecture Directory
0 [ 0] RVA [size] of Global Pointer Directory
0 [ 0] RVA [size] of Thread Storage Directory
3188 [ 40] RVA [size] of Load Configuration Directory
0 [ 0] RVA [size] of Bound Import Directory
3000 [ A8] RVA [size] of Import Address Table Directory
0 [ 0] RVA [size] of Delay Import Directory
311C [ 48] RVA [size] of COM Descriptor Directory
0 [ 0] RVA [size] of Reserved Directory
Несмотря на название, эта директория не имеет отношения к COM. Насколько я понимаю, название осталось с тех времен, когда CLR еще только начинал разрабатываться. Содержимое этой директории, или, по крайней мере, часть её содержимого, можно посмотреть с помощью утилиты CorFlags.exe из .NET SDK.
> CorFlags.exe clr_rabbit.exe
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.
Version : v2.0.50727
CLR Header: 2.5
PE : PE32
CorFlags : 0
ILONLY : 0
32BIT : 0
Signed : 0
Нас будут интересовать флаги ILONLY (только управляемый код) и 32BIT (32-х битный код), поскольку они непосредственно связаны с разрядностью сборки.
Я попробовал скомпилировать x86 и amd64 варианты стандартного “Hello World” для всех трёх значений флага /clr и посмотреть что получится. Получилась вот такая табличка:
Отсюда можно сделать вот такие выводы:
-
Флаг /clr жёстко привязывает полученный модуль к определённой платформе, так как такой модуль содержит неуправляемый код.
-
Не смотря на то, что модуль собранный с /clr:pure содержит только управляемый код, такой модуль привязан к определённой платформе не менее жестко чем предыдущий. 32-х битная система не сможет загрузить сборку “amd64, /clr:pure”, а 64-х битная будет выполнять сборку “x86, /clr:pure” в Wow64. Насколько я понимаю, причина тут в том, что такой модуль может обращаться к неуправляемому коду из других модулей.
-
Только сборки, скомпилированные с /clr:safe по-настоящему переносимы. Интересно, что для них в PE заголовке всегда будет указываться x86 в качестве целевой платформы.