> 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/uefi-start/4.library-and-hello-world.md).

# 4. 라이브러리와 Hello World

UEFI/EDKII 에서는`SystemTable` 포인터와 `SystemTable->BootServices` 그리고 주요 함수 매개변수인 `ImageHandle`을 사용하게 된다.

따라서 이러한 변수의 접근성을 위해 EDKII 라이브러리에는 `gST`, `gBS`, `gImageHandle`이 전역 선언되어 있다.\
[(https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c)](https://github.com/tianocore/edk2/blob/master/MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.c)\
[(https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiBootServicesTableLib.h)](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiBootServicesTableLib.h)

```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
  )
{
  //
  // Cache the Image Handle
  //
  gImageHandle = ImageHandle;
  ASSERT (gImageHandle != NULL);

  //
  // Cache pointer to the EFI System Table
  //
  gST = SystemTable;
  ASSERT (gST != NULL);

  //
  // Cache pointer to the EFI Boot Services Table
  //
  gBS = SystemTable->BootServices;
  ASSERT (gBS != NULL);

  return EFI_SUCCESS;
}
```

### 1. gST 전역변수를 이용한 문자열 출력하기

```
$ vi UefiLessonsPkg/HelloWorld/HelloWorld.c
-----
...
  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
+ gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
...
```

위와 같이 작성 후 다음과 같은 명령어를 이용해 빌드한다.

```
$ build --platform=UefiLessonsPkg/UefiLessonsPkg.dsc \
        --module=UefiLessonsPkg/HelloWorld/HelloWorld.inf \
        --arch=X64 \
        --buildtarget=RELEASE --tagname=GCC5
```

그럼 아래와 같은 빌드 에러가 발생한다.

```
error: ‘gST’ undeclared (first use in this function)
```

따라서 `#include`를 이용해 헤더에 라이브러리 정보를 포함 시켜야 한다.

```
$ vi UefiLessonsPkg/HelloWorld/HelloWorld.c
-----
+ #include <Library/UefiBootServicesTableLib.h>
...
```

`*.dsc` 파일에는 위 라이브러리 정보가 이미 LibraryClasses에 포함되어 있으므로 정상적으로 빌드 할 수 있으며 완성된 코드는 아래와 같다.

```
#include <Library/UefiBootServicesTableLib.h>

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
  gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
  return EFI_SUCCESS;
}
```

### 2. edk2 라이브러리 함수를 이용한 문자열 출력

```
$ vi UefiLessonsPkg/HelloWorld/HelloWorld.c
-----
+ #include <Library/UefiLib.h>
  #include <Library/UefiBootServicesTableLib.h>
...
  SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Hello World!\n");
  gST->ConOut->OutputString(gST->ConOut, L"Hello again!\n");
+ Print(L"Bye!\n");
...
```

`Print` 함수는 UefiLib 라이브러리에 포함되어 있으며 이에 대한 정보를 소스 코드와 `*.dsc` 파일에 포함해야 한다.\
[(https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiLib.h)](https://github.com/tianocore/edk2/blob/master/MdePkg/Include/Library/UefiLib.h)

만약 `*.dsc` 파일에 해당 정보를 기입하지 않을 경우 다음과 같은 에러러가 발생한다.

```
.c:821: undefined reference to `Print'
error 4000: Instance of library class [MemoryAllocationLib] is not found
```

따라서 다음과 같은 과정을 통해 LibraryClasses를 추가하도록 한다.

```
$ grep UefiLib -r ./ --include=*.inf | grep LIBRARY_CLASS
./MdePkg/Library/UefiLib/UefiLib.inf:  LIBRARY_CLASS                  = UefiLib|DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER SMM_CORE
$ grep MemoryAllocationLib -r ./ --include=*.inf | grep LIBRARY_CLASS | grep MdePkg
./MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf:  LIBRARY_CLASS                  = MemoryAllocationLib|PEIM PEI_CORE SEC
./MdePkg/Library/SmmMemoryAllocationLib/SmmMemoryAllocationLib.inf:  LIBRARY_CLASS                  = MemoryAllocationLib|DXE_SMM_DRIVER
./MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf:  LIBRARY_CLASS                  = MemoryAllocationLib|DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER UEFI_APPLICATION UEFI_DRIVER
```

```
$ vi UefiLessonsPkg/UefiLessonsPkg.dsc
-----
[LibraryClasses]
  ...
+ UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+ MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+ DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+ UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
```

또한 `Hellworld.inf` 에도 LibraryClasses 정보를 추가해준다.

```
$ vi UefiLessonsPkg/HelloWorld/HelloWorld.inf
-----
[LibraryClasses]
  UefiApplicationEntryPoint
+ UefiLib
```

이후 빌드를 진행하면 성공적인 결과를 얻을 수 있다.

```
$ cp Build/UefiLessonsPkg/RELEASE_GCC5/X64/HelloWorld.efi ~/UEFI_disk/
$ qemu-system-x86_64 -drive if=pflash,format=raw,file=Build/OvmfX64/RELEASE_GCC5/FV/OVMF.fd \
                     -drive format=raw,file=fat:rw:~/UEFI_disk \
                     -nographic \
                     -net none
                     
UEFI Interactive Shell v2.2
EDK II
UEFI v2.70 (EDK II, 0x00010000)
Mapping table
      FS0: Alias(s):HD0a1:;BLK1:
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)/HD(1,MBR,0xBE1AFDFA,0x3F,0xFBFC1)
     BLK0: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
     BLK2: Alias(s):
          PciRoot(0x0)/Pci(0x1,0x1)/Ata(0x0)
Press ESC in 1 seconds to skip startup.nsh or any other key to continue.
Shell> fs0:
FS0:\> HelloWorld.efi
Hello World!
Hello again!
Bye!
FS0:\>
```
