맨위로가기

공통 중간 언어

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

공통 중간 언어(CIL)는 CLI(Common Language Infrastructure) 프로그래밍 언어의 소스 코드를 컴파일할 때 객체 코드 대신 사용되는 CPU 및 플랫폼 독립적인 명령어 집합이다. CIL은 .NET 런타임 또는 Mono 런타임과 같이 공통 언어 인프라를 지원하는 환경에서 실행될 수 있으며, 런타임 중에 안전성이 검증되어 보안과 신뢰성을 제공한다. CIL은 스택 기반 방식을 사용하며, 로드 및 저장, 산술 연산, 형 변환, 객체 생성, 스택 관리, 제어 전송, 메서드 호출, 예외 처리, 모니터 기반 동시성 등 다양한 작업에 대한 명령어를 가진다. CIL은 컴파일러 또는 IL 어셈블러를 통해 생성되며, IL 디스어셈블러를 사용하여 코드로 다시 디스어셈블할 수 있다. CIL은 JIT 컴파일 또는 AOT 컴파일을 통해 실행될 수 있다.

2. 명칭

CIL은 원래 닷넷 언어의 베타 버전 공개 동안 마이크로소프트 중간 언어(MSIL)로 알려져 있었다. C#과 공통 언어 기반의 표준화로 인해, 바이트코드는 이제 공식적으로 CIL로 알려져 있다. 이러한 까닭으로 CIL은 아직도 MSIL로 불리는 일이 많은데, 특히 닷넷 언어를 오래 사용해 오던 사용자들에게서 이러한 경향이 두드러진다.

3. 일반 정보

CLI 프로그래밍 언어의 소스 코드를 컴파일하면, 객체 코드 대신 CIL 코드로 변환된다. CIL은 CPU 및 플랫폼에 독립적인 명령어 집합으로, 윈도우의 .NET 런타임 또는 크로스 플랫폼 Mono 런타임과 같이 공통 언어 인프라를 지원하는 모든 환경에서 실행될 수 있다. 이론적으로, 이는 서로 다른 플랫폼 및 CPU 유형에 대해 서로 다른 실행 파일을 배포할 필요가 없게 한다. CIL 코드는 런타임 중에 안전성이 검증되어 기본적으로 컴파일된 실행 파일보다 더 나은 보안과 신뢰성을 제공한다.[5][6]

4. 실행 과정

CLI 프로그래밍 언어의 소스 코드를 컴파일하면, 객체 코드 대신 CIL 코드로 변환된다. CIL은 CPU 및 플랫폼에 독립적인 명령어 집합으로, 윈도우의 .NET 런타임 또는 크로스 플랫폼 Mono 런타임과 같이 공통 언어 인프라를 지원하는 모든 환경에서 실행될 수 있다. 이론적으로, 이는 서로 다른 플랫폼 및 CPU 유형에 대해 서로 다른 실행 파일을 배포할 필요가 없게 한다. CIL 코드는 런타임 중에 안전성이 검증되어 기본적으로 컴파일된 실행 파일보다 더 나은 보안과 신뢰성을 제공한다.[5][6]

실행 과정은 다음과 같다.

# 소스 코드는 CIL 바이트 코드로 변환되고 CLI 어셈블리가 생성된다.

# CIL 어셈블리를 실행하면 해당 코드가 런타임의 JIT 컴파일러를 통해 기본 코드를 생성한다. 미리 컴파일을 사용할 수도 있는데, 이 단계를 없앨 수 있지만 실행 파일의 이식성이 저하된다.

# 컴퓨터의 프로세서가 기본 코드를 실행한다.

5. 명령어

CIL 바이트코드는 다음 작업 그룹에 대한 명령어를 가지고 있다.[5][6]

6. 계산 모델

Common Intermediate Language영어객체 지향적이며 스택 기반 방식을 사용한다. 이는 대부분의 프로그래밍 언어에서처럼 여러 레지스터나 다른 메모리 위치 대신 단일 스택에 명령어 매개변수와 결과를 저장한다는 의미이다.[5][6]

x86 어셈블리 언어와 CIL/IL를 비교하면 다음과 같다.

x86 어셈블리 vs CIL/IL
x86 어셈블리CIL/IL



x86 어셈블리에서는 `eax`와 `edx` 레지스터의 값을 더해 `eax`에 저장한다. 반면 CIL/IL 에서는 `ldloc.0`과 `ldloc.1` 명령어로 `eax`와 `edx`에 해당하는 값을 스택에 넣고, `add` 명령으로 스택에서 두 값을 꺼내 더한 후, 그 결과를 다시 스택에 넣는다. 그리고 `stloc.0` 명령으로 스택의 맨 위 값을 꺼내 `eax`에 해당하는 지역 변수 0에 저장한다.

6. 1. x86 어셈블리와의 비교

x86 어셈블리 언어로 두 숫자를 더하는 코드는 다음과 같다. 여기서 eax와 edx는 범용 레지스터를 나타낸다.[1]

```asm

add eax, edx

```

이에 해당하는 중간 언어 (IL) 코드는 다음과 같다. 여기서 0은 eax, 1은 edx를 나타낸다.[1]

```csharp

ldloc.0 // 지역 변수 0을 스택에 넣기

ldloc.1 // 지역 변수 1을 스택에 넣기

add // 스택의 맨 위 두 항목을 팝하고 더한 다음 결과를 스택에 넣기

stloc.0 // 스택의 맨 위 항목을 팝하고 지역 변수 0에 저장

```

위의 IL 코드에서, eax와 edx 레지스터 값은 먼저 스택에 푸시된다. add 명령어가 호출되면 피연산자가 팝되어 검색되고, 결과는 스택에 푸시된다. 결과 값은 다시 스택에서 팝되어 eax에 저장된다.[1]

6. 2. 객체 지향 개념

CIL은 객체 지향적으로 설계되었다. 객체를 생성하고, 메서드를 호출하며, 필드와 같은 다른 유형의 멤버를 사용할 수 있다. 모든 메서드는 (몇 가지 예외를 제외하고) 클래스 내에 존재해야 한다. 정적 메서드도 마찬가지이다.

```csharp

.class public Foo {

.method public static int32 Add(int32, int32) cil managed {

.maxstack 2

ldarg.0 // 첫 번째 인수를 로드합니다.

ldarg.1 // 두 번째 인수를 로드합니다.

add // 더합니다.

ret // 결과를 반환합니다.

}

}

```

`Add` 메서드는 `Foo`의 인스턴스를 선언할 필요가 없는데, 정적으로 선언되었기 때문이다. 따라서 C#에서 `int r = Foo.Add(2, 3); // 5` 와 같이 사용할 수 있다. CIL에서는 다음과 같이 나타난다.

```csharp

ldc.i4.2

ldc.i4.3

call int32 Foo::Add(int32, int32)

stloc.0

6. 2. 1. 인스턴스 클래스

인스턴스 클래스는 최소 하나의 생성자와 몇몇 인스턴스 멤버를 포함한다. 다음은 자동차 객체의 동작을 나타내는 메서드 집합을 보여주는 예시이다.

```csharp

.class public Car {

.method public specialname rtspecialname instance void .ctor(int32, int32) cil managed {

/* 생성자 */

}

.method public void Move(int32) cil managed { /* 구현 생략 */ }

.method public void TurnRight() cil managed { /* 구현 생략 */ }

.method public void TurnLeft() cil managed { /* 구현 생략 */ }

.method public void Brake() cil managed { /* 구현 생략 */ }

}

6. 2. 2. 객체 생성

C# 클래스 인스턴스는 `new` 키워드를 사용하여 생성된다.

```csharp

Car myCar = new Car(1, 4);

Car yourCar = new Car(1, 3);

```

위 구문은 대략 다음과 같은 CIL 명령과 같다.

```csharp

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);

6. 2. 3. 인스턴스 메서드 호출

인스턴스 메서드는 객체 인스턴스를 통해 호출된다. C#에서는 다음과 같이 호출한다.

```csharp

myCar.Move(3);

```

CIL에서는 다음과 같이 나타난다.

```csharp

ldloc.0 // "myCar" 객체를 스택에 로드

ldc.i4.3

call instance void Car::Move(int32)

6. 3. 메타데이터

공통 언어 인프라(CLI)는 컴파일된 클래스에 대한 정보를 메타데이터로 기록한다. 컴포넌트 객체 모델의 타입 라이브러리와 마찬가지로, 이는 애플리케이션이 어셈블리의 인터페이스, 클래스, 타입, 메서드 및 필드를 지원하고 검색할 수 있도록 한다. 이러한 메타데이터를 읽는 과정을 "리플렉션"이라고 한다.

메타데이터는 "속성" 형태의 데이터가 될 수 있다. 속성은 `Attribute` 클래스를 확장하여 사용자 정의할 수 있다. 이는 강력한 기능이다. 이를 통해 클래스 제작자는 클래스의 사용자가 응용 프로그램 도메인에 따라 다양한 의미 있는 방식으로 사용할 수 있는 추가 정보를 추가할 수 있다.

7. 생성

CIL 어셈블리와 명령어는 컴파일러 또는 실행 환경과 함께 제공되는 ''IL 어셈블러''(ILAsm)라는 유틸리티에 의해 생성된다.[5][6]

어셈블된 CIL은 ''IL 디스어셈블러''(ILDASM)를 사용하여 다시 코드로 디스어셈블할 수도 있다. .NET Reflector와 같은 다른 도구들은 CIL을 C# 또는 Visual Basic과 같은 고급 언어로 디컴파일할 수 있게 한다. 이는 CIL을 리버스 엔지니어링의 매우 쉬운 대상으로 만든다. 그러나 코드를 난독화하여 쉽게 읽을 수 없지만 실행 가능하게 만드는 도구도 있다.

8. 실행

CLI 프로그래밍 언어의 소스 코드를 컴파일하면, 객체 코드 대신 CIL 코드로 변환된다. CIL은 CPU 및 플랫폼에 독립적인 명령어 집합으로, 윈도우의 .NET 런타임 또는 크로스 플랫폼 Mono 런타임과 같이 공통 언어 인프라를 지원하는 모든 환경에서 실행될 수 있다. 이론적으로, 이는 서로 다른 플랫폼 및 CPU 유형에 대해 서로 다른 실행 파일을 배포할 필요가 없게 한다. CIL 코드는 런타임 중에 안전성이 검증되어 기본적으로 컴파일된 실행 파일보다 더 나은 보안과 신뢰성을 제공한다.[5][6]

실행 과정은 다음과 같다.

# 소스 코드는 CIL 바이트 코드로 변환되고 CLI 어셈블리가 생성된다.

# CIL 어셈블리를 실행하면 해당 코드가 런타임의 JIT 컴파일러를 통해 기본 코드를 생성한다. 미리 컴파일을 사용할 수도 있는데, 이 경우 실행 파일의 이식성이 저하된다.

# 컴퓨터의 프로세서가 기본 코드를 실행한다.

8. 1. Just-in-time 컴파일

Just-in-time 컴파일(JIT)은 바이트 코드를 CPU에서 즉시 실행 가능한 코드로 변환하는 것을 포함한다. 변환은 프로그램 실행 중에 점진적으로 수행된다. JIT 컴파일은 환경별 최적화, 런타임 타입 안전성, 어셈블리 검증을 제공한다. 이를 위해 JIT 컴파일러는 불법적인 접근을 위해 어셈블리 메타데이터를 검사하고 위반 사항을 적절하게 처리한다.[5][6]

8. 2. Ahead-of-time 컴파일

CLI 호환 실행 환경은 런타임 시 JIT 프로세스를 제거하여 어셈블리의 AOT 컴파일을 수행하여 더 빠르게 실행할 수 있는 옵션도 제공한다.

.NET Framework에는 AOT를 수행하는 네이티브 이미지 생성기 (NGEN)라는 특수 도구가 있다. AOT에 대한 다른 접근 방식으로는 런타임에 의존하지 않고 .Net Core 코드를 단일 실행 파일로 컴파일할 수 있는 CoreRT가 있다. 모노에도 AOT를 수행하는 옵션이 있다.

9. 포인터 명령어 - C++/CLI

CIL은 C/C++ 코드를 CIL로 컴파일하는 데 필요한 데이터/함수 포인터 조작에 충분한 `ldind`, `stind`, `ldloca` 및 많은 호출 명령을 제공한다.

아래는 C++ 코드 예시이다.

```cpp

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();

}

```

위 C++ 코드 예시에 대응하는 CIL 코드는 아래와 같다.

```cpp

.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

10. 예제

헬로 월드 프로그램 예제는 다음과 같다.[10]

```csharp

.assembly Hello {}

.assembly extern mscorlib {}

.method static void Main()

{

.entrypoint

.maxstack 1

ldstr "Hello, world!"

call void [mscorlib]System.Console::WriteLine(string)

ret

}

```

다음 코드는 더 많은 수의 연산 코드를 가지고 있으며, 자바 바이트코드에 대한 기사에서 해당 코드와 비교할 수 있다.

```csharp

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 어셈블러 구문으로는 다음과 같다.

```csharp

.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 파일(Portable Executable)이다.

참조

[1] 웹사이트 Intermediate Language & execution https://docs.microso[...] 2023-04-19
[2] 웹사이트 ECMA-335 Common Language Infrastructure (CLI) https://ecma-interna[...]
[3] 웹사이트 What is Intermediate Language(IL)/MSIL/CIL in .NET http://www.interview[...] 2011-02-17
[4] 웹사이트 HackTool:MSIL/SkypeCracker https://www.microsof[...] Microsoft 2019-11-26
[5] 서적 Benefits of CIL https://books.google[...] Apress 2009-05-02
[6] 웹사이트 Unmanaged, Managed Extensions for C++, Managed and .Net Framework https://www.visualcp[...] 2020-07-07
[7] 웹사이트 What is Intermediate Language(IL)/MSIL/CIL in .NET http://www.interview[...] 2011-02-17
[8] 서적 Benefits of CIL https://books.google[...] 2011-02-17
[9] 문서 주된 닷넷 언어는 [[C 샤프|C#]], [[비주얼 베이직 닷넷]], [[C++/CLI]], [[J 샤프|J#]]가 있다.
[10] 위키 en:wikibooks:Computer Programming/Hello world#CIL



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com