> For the complete documentation index, see [llms.txt](https://bob-mcd-team.gitbook.io/uefi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bob-mcd-team.gitbook.io/uefi/uefi-development/driver-library/36.library-constructor-destructor.md).

# 36. Library의 constructor와 destructor, NULL Library

## CONSTRUCTOR

새 라이브러리 모듈을 만든다.

```
$ vi UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf
---
[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = SimpleLibraryWithConstructor
  FILE_GUID                      = 96952c1e-86a6-4700-96b0-e7303ac3f92d
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  LIBRARY_CLASS                  = SimpleLibrary | UEFI_APPLICATION
  CONSTRUCTOR                    = SimpleLibraryConstructor                  

[Sources]
  SimpleLibraryWithConstructor.c

[Packages]
  MdePkg/MdePkg.dec
  UefiLessonsPkg/UefiLessonsPkg.dec
```

여기에 `CONSTRUCTOR`에 생성자 함수라는 이름의 명령문을 추가했다. 이것이 언제 실행되는지 알기 위해 `Print` 명령문을 추가해보자.

```
$ vi UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.c
---
#include <Library/UefiLib.h>
#include <Library/SimpleLibrary.h>

UINTN Plus2(UINTN number) {
  return number+2;
}

EFI_STATUS
EFIAPI
SimpleLibraryConstructor(
  IN EFI_HANDLE       ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  Print(L"Hello from library constructor!\n");
  return EFI_SUCCESS;
}
```

이제 새 라이브러리를 사용하는 다른 애플리ㅇ케이션을 만들 필요가 없다. 이제는 단순히 `UefiLessonsPkg/UefiLessonsPkg.dsc`에서 라이브러리 설정을 변경하면, `SimpleLibraryUser`가 새로운 라이브러리 버전으로 다시 컴파일된다.

다른 부분은 건드리지 않아도 괜찮다.

```
[LibraryClasses]
  ...
  #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf
  SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf
```

만약 이 앱을 빌드하고, OVMF를 실행하면, 다음과 같은 화면이 나올 것이다.

```
FS0:\> SimpleLibraryUser.efi
Hello from library constructor!
5
```

생성자를 사용하는 라이브러리의 예로는 `UefiBootServicesTableLib` 라이브러리가 있다. <https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf>

우리는 이를 이전 장들에서 모두 사용해 보았으니, 한 번 사용해보도록 하자.

사실, 이 라이브러리가 가지고 있는 것은 생성자일 뿐이다.

이 라이브러리의 작동 방식을 이해하려면, \*.c 및 \*.h 파일을 확인해보자.\
<https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c>

```
EFI_HANDLE         gImageHandle = NULL;
EFI_SYSTEM_TABLE   *gST         = NULL;
EFI_BOOT_SERVICES  *gBS         = NULL;

EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  gImageHandle = ImageHandle;
  gST = SystemTable;
  gBS = SystemTable->BootServices;

  return EFI_SUCCESS;
}
```

<https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c>

```
extern EFI_HANDLE         gImageHandle;
extern EFI_SYSTEM_TABLE   *gST;
extern EFI_BOOT_SERVICES  *gBS;
```

이 라이브러리는 UEFI의 main 부분에 대한 바로가기와 같은 몇 가지 전역 변수를 설정한다. 라이브러리 생성자가 Main 앱 코드이전에 실행되므로, 이 라이브러리를 사용하여 `gImageHandle`/`gST`/`gBS` 모든 부분에 접근할 수 있다.

## DESTRUCTOR

마찬가지로, 생성자와 소멸자가 모두 존재하는 `SimpleLibrary`의 다른 버전을 만들 수 있다.

```
$ vi UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.inf
---
[Defines]
  INF_VERSION                    = 1.25
  BASE_NAME                      = SimpleLibraryWithConstructorAndDestructor
  FILE_GUID                      = 96952c1e-86a6-4700-96b0-e7303ac3f92d
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 1.0
  LIBRARY_CLASS                  = SimpleLibrary | UEFI_APPLICATION
  CONSTRUCTOR                    = SimpleLibraryConstructor
  DESTRUCTOR                     = SimpleLibraryDestructor                      

[Sources]
  SimpleLibraryWithConstructorAndDestructor.c

[Packages]
  MdePkg/MdePkg.dec
  UefiLessonsPkg/UefiLessonsPkg.dec
```

```c
$ vi UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.c
---
#include <Library/UefiLib.h>
#include <Library/SimpleLibrary.h>

UINTN Plus2(UINTN number) {
  return number+2;
}

EFI_STATUS
EFIAPI
SimpleLibraryConstructor(
  IN EFI_HANDLE       ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  Print(L"Hello from library constructor!\n");
  return EFI_SUCCESS;
}

EFI_STATUS
EFIAPI
SimpleLibraryDestructor(
  IN EFI_HANDLE       ImageHandle,
  IN EFI_SYSTEM_TABLE *SystemTable
  )
{
  Print(L"Hello from library destructor!\n");
  return EFI_SUCCESS;
}
```

`UefiLessonsPkg/UefiLessonsPkg.dsc`의 끝부분을 바꾸는 것을 잊지 말도록 하자.

```
[LibraryClasses]
  ...
  #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf
  #SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf
  SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.inf
```

이제 애플리케이션을 실행하면, 처음과 끝에 모두 문자열을 출력할 것이다.

```
FS0:\> SimpleLibraryUser.efi
Hello from library constructor!
5
Hello from library destructor!
```

생성자와 소멸자가 모두 있는 라이브러리의 예로, `DebugLibConstructor.c`를 살펴보자.\
<https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiDebugLibConOut/DebugLibConstructor.c>

이 라이브러리는 생성자에서 `CreateEvent`를 사용하고, `CloseEvent`를 사용하여 소멸자에서 닫는다.

## NULL Library

이미 언급했었다시피, OVMF는 `Shell` 앱자체를 포함하고 있다. 컴파일을 위해 OVMF 패키지 DSC(`OvmfPkgX64.dsc`) 파일에는 다음 문자열이 포함되어 있다.

```
[Components]
  ...
  ShellPkg/Application/Shell/Shell.inf {
    <LibraryClasses>
      ShellCommandLib|ShellPkg/Library/UefiShellCommandLib/UefiShellCommandLib.inf
      NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellDriver1CommandsLib/UefiShellDriver1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf
      ...
      HandleParsingLib|ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf
      PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
      BcfgCommandLib|ShellPkg/Library/UefiShellBcfgCommandLib/UefiShellBcfgCommandLib.inf

      ... 
  }
```

여기서 일부 라이브러리 클래스에 `NULL` 클래스가 있음을 알 수 있다.

`NULL` 라이브러리 클래스는 개념적으로 "anonymous library, 익명 라이브러리"이다. 모듈이 라이브러리의 함수를 직접 호출하지 않더라도 모듈에 코드를 정적으로 연결할 수 있다.

앱에서 라이브러리 API를 호출할 필요 없이 라이브러리 생성자/소멸자기능만 필요한 경우 유용할 수 있다.\
<https://github.com/tianocore/edk2/blob/master/ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.c>

해당 파일을 보면, 이 모듈의 생성자가 shell에 명령을 추가하는 데 사용된다는 것을 알 수 있다.

```
EFI_STATUS
EFIAPI
ShellLevel1CommandsLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
 ...
  ShellCommandRegisterCommandName(L"stall",  ShellCommandRunStall   , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_STALL) ));
  ShellCommandRegisterCommandName(L"for",    ShellCommandRunFor     , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_FOR)   ));
  ShellCommandRegisterCommandName(L"goto",   ShellCommandRunGoto    , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_GOTO)  ));
  ShellCommandRegisterCommandName(L"if",     ShellCommandRunIf      , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_IF)    ));
  ShellCommandRegisterCommandName(L"shift",  ShellCommandRunShift   , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_SHIFT) ));
  ShellCommandRegisterCommandName(L"exit",   ShellCommandRunExit    , ShellCommandGetManFileNameLevel1, 1, L"", TRUE , gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_EXIT)  ));
  ShellCommandRegisterCommandName(L"else",   ShellCommandRunElse    , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_ELSE)  ));
  ShellCommandRegisterCommandName(L"endif",  ShellCommandRunEndIf   , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_ENDIF) ));
  ShellCommandRegisterCommandName(L"endfor", ShellCommandRunEndFor  , ShellCommandGetManFileNameLevel1, 1, L"", FALSE, gShellLevel1HiiHandle, (EFI_STRING_ID)(PcdGet8(PcdShellSupportLevel) < 3 ? 0 : STRING_TOKEN(STR_GET_HELP_ENDFOR)));

  return (EFI_SUCCESS);
```

위의 방법은 Shell command support를 다른 모듈로 분할하는 꽤나 괜찮은 방법이다. 이 기능을 사용하면 필요한 command support를 통해 `Shell` 이미지를 쉽게 컴파일할 수 있다.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bob-mcd-team.gitbook.io/uefi/uefi-development/driver-library/36.library-constructor-destructor.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
