CONSTRUCTOR
새 라이브러리 모듈을 만든다.
Copy $ 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
명령문을 추가해보자.
Copy $ 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
가 새로운 라이브러리 버전으로 다시 컴파일된다.
다른 부분은 건드리지 않아도 괜찮다.
Copy [LibraryClasses]
...
#SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf
SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf
만약 이 앱을 빌드하고, OVMF를 실행하면, 다음과 같은 화면이 나올 것이다.
Copy 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
Copy 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
Copy extern EFI_HANDLE gImageHandle;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_BOOT_SERVICES *gBS;
이 라이브러리는 UEFI의 main 부분에 대한 바로가기와 같은 몇 가지 전역 변수를 설정한다. 라이브러리 생성자가 Main 앱 코드이전에 실행되므로, 이 라이브러리를 사용하여 gImageHandle
/gST
/gBS
모든 부분에 접근할 수 있다.
DESTRUCTOR
마찬가지로, 생성자와 소멸자가 모두 존재하는 SimpleLibrary
의 다른 버전을 만들 수 있다.
Copy $ 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
Copy $ 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
의 끝부분을 바꾸는 것을 잊지 말도록 하자.
Copy [LibraryClasses]
...
#SimpleLibrary|UefiLessonsPkg/Library/SimpleLibrary/SimpleLibrary.inf
#SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructor/SimpleLibraryWithConstructor.inf
SimpleLibrary|UefiLessonsPkg/Library/SimpleLibraryWithConstructorAndDestructor/SimpleLibraryWithConstructorAndDestructor.inf
이제 애플리케이션을 실행하면, 처음과 끝에 모두 문자열을 출력할 것이다.
Copy 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
) 파일에는 다음 문자열이 포함되어 있다.
Copy [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에 명령을 추가하는 데 사용된다는 것을 알 수 있다.
Copy 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
이미지를 쉽게 컴파일할 수 있다.