Общий промежуточный язык
Эта статья нуждается в дополнительных цитатах для проверки . ( ноябрь 2017 г. ) |
Common Intermediate Language ( CIL ), ранее называвшийся Microsoft Intermediate Language ( MSIL ) или Intermediate Language ( IL ), [1] — это набор двоичных команд промежуточного языка , определенный в спецификации Common Language Infrastructure (CLI). [2] Инструкции CIL выполняются CIL-совместимой средой выполнения, такой как Common Language Runtime . Языки, ориентированные на CLI, компилируются в CIL. CIL — это объектно-ориентированный байт на основе стека -код . Среды выполнения обычно по принципу «точно в срок» компилируют CIL-инструкции в собственный код .
Первоначально CIL был известен как Microsoft Intermediate Language (MSIL) во время бета-выпусков языков .NET. Благодаря стандартизации C# и CLI байт-код теперь официально известен как CIL. [3] В определениях вирусов Защитника Windows двоичные файлы, скомпилированные с его помощью, продолжают называться MSIL. [4]
Общая информация [ править ]
Во время компиляции языков программирования CLI исходный код преобразуется в код CIL, а не в объектный код, специфичный для платформы или процессора . CIL — это независимый от процессора и платформы набор инструкций, который может выполняться в любой среде, поддерживающей Common Language Infrastructure, например, в среде выполнения .NET в Windows или в кроссплатформенной среде выполнения Mono . Теоретически это избавляет от необходимости распространять разные исполняемые файлы для разных платформ и типов процессоров. Код CIL проверяется на безопасность во время выполнения, обеспечивая лучшую безопасность и надежность, чем исполняемые файлы, скомпилированные в собственном коде. [5] [6]
Процесс выполнения выглядит следующим образом:
- Исходный код преобразуется в байт-код CIL и сборка CLI . создается
- При выполнении сборки CIL ее код передается через JIT-компилятор среды выполнения для создания собственного кода. Также можно использовать предварительную компиляцию, которая исключает этот шаг, но за счет переносимости исполняемого файла.
- Процессор компьютера выполняет собственный код.
Инструкция [ править ]
Байт-код CIL содержит инструкции для следующих групп задач:
- Загрузите и сохраните
- Арифметика
- Преобразование типов
- Создание и манипулирование объектами
- Управление стеком операндов (push/pop)
- Передача управления (ветвление)
- Вызов метода и возврат
- Выброс исключений
- Параллелизм на основе монитора
- Манипуляции с указателями данных и функций, необходимые для C++/CLI и небезопасного кода C#.
Вычислительная модель [ править ]
Common Intermediate Language является объектно-ориентированным и основанным на стеке , что означает, что параметры и результаты инструкций хранятся в одном стеке, а не в нескольких регистрах или других местах памяти, как в большинстве языков программирования .
Код, добавляющий два числа на языке ассемблера x86 , где eax и edx указывают два разных регистра общего назначения :
add eax, edx
Код на промежуточном языке (IL), где 0 — eax, а 1 — edx:
ldloc.0 // push local variable 0 onto stack
ldloc.1 // push local variable 1 onto stack
add // pop and add the top two stack items then push the result onto the stack
stloc.0 // pop and store the top stack item to local variable 0
В последнем примере значения двух регистров, eax и edx, сначала помещаются в стек. Когда вызывается инструкция добавления, операнды «извлекаются» или извлекаются, а результат «помещается» или сохраняется в стеке. Полученное значение затем извлекается из стека и сохраняется в eax.
Объектно-ориентированные концепции [ править ]
CIL спроектирован как объектно-ориентированный. Вы можете создавать объекты, вызывать методы и использовать другие типы членов, например поля.
Каждый метод должен (за некоторыми исключениями) находиться в классе. То же самое делает этот статический метод:
.class public Foo {
.method public static int32 Add(int32, int32) cil managed {
.maxstack 2
ldarg.0 // load the first argument;
ldarg.1 // load the second argument;
add // add them;
ret // return the result;
}
}
Метод Add не требует объявления какого-либо экземпляра Foo, поскольку он объявлен как статический, и затем его можно использовать в C# следующим образом:
int r = Foo.Add(2, 3); // 5
В CIL это будет выглядеть так:
ldc.i4.2
ldc.i4.3
call int32 Foo::Add(int32, int32)
stloc.0
Классы экземпляров [ править ]
Класс экземпляра содержит по крайней мере один конструктор и несколько членов экземпляра . Следующий класс имеет набор методов, представляющих действия объекта Car.
.class public Car {
.method public specialname rtspecialname instance void .ctor(int32, int32) cil managed {
/* Constructor */
}
.method public void Move(int32) cil managed { /* Omitting implementation */ }
.method public void TurnRight() cil managed { /* Omitting implementation */ }
.method public void TurnLeft() cil managed { /* Omitting implementation */ }
.method public void Brake() cil managed { /* Omitting implementation */ }
}
Создание объектов [ править ]
В C# экземпляры классов создаются следующим образом:
Car myCar = new Car(1, 4);
Car yourCar = new Car(1, 3);
И эти утверждения примерно такие же, как и эти инструкции в CIL:
ldc.i4.1
ldc.i4.4
newobj instance void Car::.ctor(int, int)
stloc.0 // myCar = new Car(1, 4);
ldc.i4.1
ldc.i4.3
newobj instance void Car::.ctor(int, int)
stloc.1 // yourCar = new Car(1, 3);
Вызов методов экземпляра [ править ]
Методы экземпляра вызываются в C# следующим образом:
myCar.Move(3);
Как вызывается в CIL:
ldloc.0 // Load the object "myCar" on the stack
ldc.i4.3
call instance void Car::Move(int32)
Метаданные [ править ]
Common Language Infrastructure (CLI) записывает информацию о скомпилированных классах в виде метаданных . Подобно библиотеке типов в объектной модели компонентов , это позволяет приложениям поддерживать и обнаруживать интерфейсы, классы, типы, методы и поля в сборке. Процесс чтения таких метаданных называется « отражением ».
Метаданные могут представлять собой данные в форме «атрибутов». Атрибуты можно настроить, расширив Attribute
сорт. Это мощная функция. Это дает создателю класса возможность дополнить его дополнительной информацией, которую потребители класса могут использовать различными значимыми способами, в зависимости от предметной области приложения.
Пример [ править ]
Ниже приведен базовый текст «Hello, World!» программа, написанная на ассемблере CIL. Он отобразит строку «Привет, мир!».
.assembly Hello {}
.assembly extern mscorlib {}
.method static void Main()
{
.entrypoint
.maxstack 1
ldstr "Hello, world!"
call void [mscorlib]System.Console::WriteLine(string)
ret
}
Следующий код более сложен по количеству кодов операций.
Этот код также можно сравнить с соответствующим кодом в статье про байт-код Java .
static void Main(string[] args)
{
for (int i = 2; i < 1000; i++)
{
for (int j = 2; j < i; j++)
{
if (i % j == 0)
goto outer;
}
Console.WriteLine(i);
outer:;
}
}
В синтаксисе ассемблера CIL это выглядит так:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (int32 V_0,
int32 V_1)
ldc.i4.2
stloc.0
br.s IL_001f
IL_0004: ldc.i4.2
stloc.1
br.s IL_0011
IL_0008: ldloc.0
ldloc.1
rem
brfalse.s IL_001b
ldloc.1
ldc.i4.1
add
stloc.1
IL_0011: ldloc.1
ldloc.0
blt.s IL_0008
ldloc.0
call void [mscorlib]System.Console::WriteLine(int32)
IL_001b: ldloc.0
ldc.i4.1
add
stloc.0
IL_001f: ldloc.0
ldc.i4 0x3e8
blt.s IL_0004
ret
}
Это всего лишь представление того, как CIL выглядит на уровне виртуальной машины (VM). При компиляции методы сохраняются в таблицах, а инструкции сохраняются в виде байтов внутри сборки, которая представляет собой переносимый исполняемый файл (PE).
Поколение [ править ]
Сборка CIL и инструкции генерируются либо компилятором, либо утилитой, называемой IL Assembler ( ILAsm ), которая поставляется со средой выполнения.
Собранный CIL также можно снова дизассемблировать в код с помощью дизассемблера IL (ILDASM). Существуют и другие инструменты, такие как .NET Reflector , которые могут декомпилировать CIL в язык высокого уровня (например, C# или Visual Basic ). Это делает CIL очень легкой целью для обратного проектирования. Эта черта является общей для байт-кода Java . Однако существуют инструменты, которые могут запутать код и сделать это так, что код не может быть легко читаемым, но при этом остается работоспособным.
Исполнение [ править ]
Сборка точно в срок [ править ]
Компиляция «точно в срок» (JIT) включает в себя преобразование байт-кода в код, который немедленно исполняется процессором. Преобразование выполняется постепенно во время выполнения программы. JIT-компиляция обеспечивает оптимизацию для конкретной среды, безопасность типов во время выполнения и проверку сборки. Для этого JIT-компилятор проверяет метаданные сборки на предмет несанкционированного доступа и соответствующим образом обрабатывает нарушения.
Предварительная компиляция [ править ]
Среды выполнения, совместимые с CLI, также имеют возможность выполнять предварительную компиляцию (AOT) сборки, чтобы ускорить ее выполнение за счет удаления процесса JIT во время выполнения.
В .NET Framework есть специальный инструмент под названием Native Image Generator (NGEN), который выполняет AOT. Другой подход к AOT — CoreRT , который позволяет компилировать код .Net Core в один исполняемый файл без зависимости от среды выполнения. В Mono также есть возможность выполнить AOT.
Инструкции указателя — C++/CLI [ править ]
Заметным отличием от байт-кода Java является то, что CIL поставляется с ldind
, stind
, ldloca
и множество инструкций вызова, которых достаточно для манипулирования указателями данных/функций, необходимых для компиляции кода C/C++ в CIL.
class A {
public: virtual void __stdcall meth() {}
};
void test_pointer_operations(int param) {
int k = 0;
int * ptr = &k;
*ptr = 1;
ptr = ¶m;
*ptr = 2;
A a;
A * ptra = &a;
ptra->meth();
}
Соответствующий код в CIL можно отобразить следующим образом:
.method assembly static void modopt([mscorlib]System.Runtime.CompilerServices.CallConvCdecl)
test_pointer_operations(int32 param) cil managed
{
.vtentry 1 : 1
// Code size 44 (0x2c)
.maxstack 2
.locals ([0] int32* ptr,
[1] valuetype A* V_1,
[2] valuetype A* a,
[3] int32 k)
// k = 0;
IL_0000: ldc.i4.0
IL_0001: stloc.3
// ptr = &k;
IL_0002: ldloca.s k // load local's address instruction
IL_0004: stloc.0
// *ptr = 1;
IL_0005: ldloc.0
IL_0006: ldc.i4.1
IL_0007: stind.i4 // indirection instruction
// ptr = ¶m
IL_0008: ldarga.s param // load parameter's address instruction
IL_000a: stloc.0
// *ptr = 2
IL_000b: ldloc.0
IL_000c: ldc.i4.2
IL_000d: stind.i4
// a = new A;
IL_000e: ldloca.s a
IL_0010: call valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.CallConvThiscall) 'A.{ctor}'(valuetype A* modopt([mscorlib]System.Runtime.CompilerServices.IsConst) modopt([mscorlib]System.Runtime.CompilerServices.IsConst))
IL_0015: pop
// ptra = &a;
IL_0016: ldloca.s a
IL_0018: stloc.1
// ptra->meth();
IL_0019: ldloc.1
IL_001a: dup
IL_001b: ldind.i4 // reading the VMT for virtual call
IL_001c: ldind.i4
IL_001d: calli unmanaged stdcall void modopt([mscorlib]System.Runtime.CompilerServices.CallConvStdcall)(native int)
IL_0022: ret
} // end of method 'Global Functions'::test_pointer_operations
См. также [ править ]
Ссылки [ править ]
- ^ «Промежуточный язык и исполнение» .
- ^ «Инфраструктура общего языка ECMA-335 (CLI)» .
- ^ «Что такое промежуточный язык (IL)/MSIL/CIL в .NET» . Проверено 17 февраля 2011 г.
CIL: ...Когда мы компилируем [a]. NET, он [преобразовывается] не напрямую в двоичный код, а в промежуточный язык. При запуске проекта каждый язык программирования .NET преобразуется в двоичный код CIL. Только некоторая часть CIL, необходимая во время выполнения, преобразуется в двоичный код. DLL и EXE .NET также находятся в форме CIL.
- ^ «HackTool:MSIL/SkypeCracker» . Майкрософт . Проверено 26 ноября 2019 г. .
- ^ Троэльсен, Эндрю (2 мая 2009 г.). Преимущества CIL . ISBN 9781590598849 . Проверено 17 февраля 2011 г.
- ^ «Неуправляемые и управляемые расширения для C++, управляемых и .Net Framework» . www.visualcplusdotnet.com . Проверено 7 июля 2020 г.
Дальнейшее чтение [ править ]
- Бок, Джейсон (2002). CIL-программирование: под капотом .NET . Апресс. ISBN 978-1590590416 .