# 50.UEFI\_HII\_RESOURCE\_SECTION을 사용하여 문자열 패키지와 함께 HII 패키지 목록 게시하기

이번 장에서는 애플리케이션이 HII 문자열 패키지를 게시할 수 있는 또 다른 방법을 살펴본다. 이번에는 결과 EFI 파일 PE/COFF 리소스에 HII 데이터를 포함하는 것에 대해 알아본다.

## 애플리케이션 만들기

스크립트를 써서 평소와 같이 애플리케이션을 만든다.

```shell
./createNewApp.sh HIIStringsUNIRC
```

우리의 DSC 패키지 파일 UefiLessonsPkg/UefiLessonsPkg.dsc에 추가한다.

```
[Components]
  ...
  UefiLessonsPkg/HIIStringsUNI/HIIStringsUNI.inf
```

마지막으로 패키지 목록에 대한 GUID가 필요하며 패키지 DEC 파일인 UefiLessonsPkg/UefiLessonsPkg.dec에서 선언해준다.

```
[Guids]
  ...
  gHIIStringsUNIRCGuid = { 0x785693b4, 0x623e, 0x40fa, { 0x9a, 0x45, 0x68, 0xda, 0x38, 0x30, 0x89, 0xdd }}
```

이제 `HIIStringsUNI` 앱과 유사하도록 애플리케이션 파일을 수정해준다. `Strings.uni` 파일을 만들고 애플리케이션의 INF 및 \*.c 파일을 수정해야한다.

* `UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC.inf`
* `UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC.c`
* `UefiLessonsPkg/HIIStringsUNIRC/Strings.uni`

모든 작업을 완료하면 `HIIStringsUNI` 애플리케이션과 동일한 결과를 얻을 수 있다.

```
FS0:\> HIIStringsUNIRC.efi
en-US ID=1: English
en-US ID=2: Hello!
en-US ID=3: Bye!
fr-FR ID=1: Francais
fr-FR ID=2: Bonjour!
fr-FR ID=3: Au revoir!
Best language ID=1: English
Best language ID=2: Hello!
Best language ID=3: Bye!
fr ID=3: Au revoir!
```

이제 애플리케이션을 수정할 준비가 다 되었다.

## UEFI\_HII\_RESOURCE\_SECTION

`UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC.inf`에 다음을 추가한다.

```
[Defines]
  ...
  UEFI_HII_RESOURCE_SECTION      = TRUE
```

`UEFI_HII_RESOURCE_SECTION` 플래그는 HII 리소스 섹션이 PE 이미지로 생성되는지 여부를 지정한다. 애플리케이션을 빌드하면 다음 오류로 인해 애플리케이션을 빌드할 수 없다.

```
UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC.c:15:42: error: ‘HIIStringsUNIRCStrings’ undeclared (first use in this function);
```

`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC/DEBUG/HIIStringsUNIRCStrDefs.h`를 보면 이 파일에 여전히 문자열 토큰이 포함되어 있지만 `HIIStringsUNIRCStrings`는 더 이상 여기에 존재하지 않는 것을 알 수 있다.

그리고 `AutoGen.c` 파일에는 더 이상 `HIIStringsUNIRCStrings` 배열 초기화 코드가 포함되어 있지 않다(`Build/UefiLessonsPkg/RELEASE_GCC5/X64/UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC/DEBUG/AutoGen.c`).

이제 우리의 문자열이 결과로 나오는 \*.efi 이미지의 특수 섹션으로 인코딩되어 들어가는 것이다.

이를 우리가 얻으려면 애플리케이션 `EFI_HANDLE ImageHandle`에서 프로토콜 `EFI_HII_PACKAGE_LIST_PROTOCOL`을 검색해야 한다.

다음은 Shell이 모든 프로그램을 로드하는 데 사용하는 `EFI_BOOT_SERVICES.LoadImage()` 함수에 대한 UEFI 스펙의 관련 내용이다.

```
Once the image is loaded, LoadImage() installs EFI_HII_PACKAGE_LIST_PROTOCOL on the handle if
the image contains a custom PE/COFF resource with the type 'HII'. The protocol's interface pointer points
to the HII package list which is contained in the resource's data
```

<https://github.com/tianocore/edk2/blob/master/MdePkg/MdePkg.dec>에 따르면 `EFI_HII_PACKAGE_LIST_PROTOCOL`은 `gEfiHiiPackageListProtocolGuid`로 식별된다.

```
[Protocols]
  ...
  ## Include/Protocol/HiiPackageList.h
  gEfiHiiPackageListProtocolGuid  = { 0x6a1ee763, 0xd47a, 0x43b4, {0xaa, 0xbe, 0xef, 0x1d, 0xe2, 0xab, 0x56, 0xfc}}
```

우리의 INF 파일의 `[Packages]` 섹션에 `MdeModulePkg/MdeModulePkg.dec`가 이미 있으므로 이 GUID를 `[Protocols]` 섹션에 추가하기만 하면 된다.

```
[Protocols]
  gEfiHiiPackageListProtocolGuid
```

코드에서는 `OpenProtocol` UEFI 부트 서비스의 도움으로 애플리케이션의 `ImageHandle` 에서 `PackageList` 프로토콜을 얻을 수 있다.

```c
...

EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS Status;
  EFI_HII_PACKAGE_LIST_HEADER *PackageList;
  //
  // Retrieve HII package list from ImageHandle.
  //
  Status = gBS->OpenProtocol (
                  ImageHandle,
                  &gEfiHiiPackageListProtocolGuid,
                  (VOID **)&PackageList,
                  ImageHandle,
                  NULL,
                  EFI_OPEN_PROTOCOL_GET_PROTOCOL
                  );
  if (EFI_ERROR (Status)) {
    Print(L"Error! Can't open EFI_HII_PACKAGE_LIST_PROTOCOL\n");
    return Status;
  }
  ...
```

결과의 `PackageList`는 `HIIStringsUNI` 애플리케이션의 경우와 같이 `StrGather` 스크립트의 결과인 일부 size 헤더가 있는 문자열 패키지가 아니다. `HIIStringsC` 애플리케이션에서 수동으로 구성한 것과 같은 일반 `PackageList`이다. 따라서 `HiiAddPackages` 라이브러리 함수를 사용하는 대신 `EFI_HII_DATABASE_PROTOCOL.NewPackageList()`를 직접 사용해야 한다.

```c
EFI_HII_HANDLE Handle;
Status = gHiiDatabase->NewPackageList(gHiiDatabase, PackageList, NULL, &Handle);
if (EFI_ERROR(Status))
{
  Print(L"Can't register HII Package list %g, status = %r\n", gHIIStringsUNIRCGuid, Status);
  return Status;
}
```

여기서는 `gHiiDatabase`를 사용했으므로 필요한 헤더인 `#include <Library/UefiHiiServicesLib.h>`를 추가하고 애플리케이션 INF 파일의 `[LibraryClasses]`에 `UefiHiiServicesLib`를 추가하는 것을 잊지 말자.

이제 애플리케이션을 빌드하고 실행해보면 모든 것이 정상인것을 확인할 수 있다.

```
FS0:\> HIIStringsUNIRC.efi
Status = Success
en-US ID=1: English
en-US ID=2: Hello!
en-US ID=3: Bye!
fr-FR ID=1: Francais
fr-FR ID=2: Bonjour!
fr-FR ID=3: Au revoir!
Best language ID=1: English
Best language ID=2: Hello!
Best language ID=3: Bye!
fr ID=3: Au revoir!
```

## 유형이 'HII'인 PE/COFF 리소스

`objdump`를 사용하여 애플리케이션 헤더를 살펴보자. `-x` 옵션을 사용하여 모든 헤더의 내용을 출력할 수 있다.

```
objdump -x  Build/UefiLessonsPkg/RELEASE_GCC5/X64/HIIStringsUNIRC.efi
```

화살표 뒤의 사항에 주의하면서 보자.

```
...

The Data Directory
Entry 0 0000000000000000 00000000 Export Directory [.edata (or where ever we found it)]
Entry 1 0000000000000000 00000000 Import Directory [parts of .idata]
Entry 2 00000000000023c0 00000180 Resource Directory [.rsrc]                <---------- Resource Directory 에 데이터가 존재한다.
Entry 3 0000000000000000 00000000 Exception Directory [.pdata]
Entry 4 0000000000000000 00000000 Security Directory
Entry 5 0000000000000000 00000000 Base Relocation Directory [.reloc]
Entry 6 00000000000022cc 0000001c Debug Directory
Entry 7 0000000000000000 00000000 Description Directory
Entry 8 0000000000000000 00000000 Special Directory
Entry 9 0000000000000000 00000000 Thread Storage Directory [.tls]
Entry a 0000000000000000 00000000 Load Configuration Directory
Entry b 0000000000000000 00000000 Bound Import Directory
Entry c 0000000000000000 00000000 Import Address Table Directory
Entry d 0000000000000000 00000000 Delay Import Directory
Entry e 0000000000000000 00000000 CLR Runtime Header
Entry f 0000000000000000 00000000 Reserved

...

The .rsrc Resource Directory section:
000  Type Table: Char: 0, Time: 00000000, Ver: 0/0, Num Names: 1, IDs: 0
010   Entry: name: [val: 80000048 len 3]: HII, Value: 0x80000018           <--------- Data 타입이 HII이다.
018    Name Table: Char: 0, Time: 00000000, Ver: 0/0, Num Names: 1, IDs: 0
028     Entry: name: [val: 80000050 len 3]: EFI, Value: 0x80000030
030      Language Table: Char: 0, Time: 00000000, Ver: 0/0, Num Names: 1, IDs: 0
040       Entry: name: [val: 80000058 len 3]: BIN, Value: 0x000060
060        Leaf: Addr: 0x002430, Size: 0x0000ea, Codepage: 0
 String table starts at offset: 0x48
 Resources start at offset: 0x70

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00001fc0  0000000000000240  0000000000000240  00000240  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         000001c0  0000000000002200  0000000000002200  00002200  2**4
                  CONTENTS, ALLOC, LOAD, DATA
  2 .rsrc         00000180  00000000000023c0  00000000000023c0  000023c0  2**2       <----- 여기에도 .rsrc가 표시된다.
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
SYMBOL TABLE:
no symbols
```

이제 `UefiLessonsPkg/HIIStringsUNIRC/HIIStringsUNIRC.inf`에 `UEFI_HII_RESOURCE_SECTION`을 주석 처리해준다.

```
[Defines]
  ...
  #UEFI_HII_RESOURCE_SECTION      = TRUE
```

애플리케이션 빌드 후에 objdump를 다시 한 번 실행해보면 다음과 같은 결과를 얻을 수 있다.

```
...

The Data Directory
Entry 0 0000000000000000 00000000 Export Directory [.edata (or where ever we found it)]
Entry 1 0000000000000000 00000000 Import Directory [parts of .idata]
Entry 2 0000000000000000 00000000 Resource Directory [.rsrc]                 <----- Resource directory 가 비어있다.
Entry 3 0000000000000000 00000000 Exception Directory [.pdata]
Entry 4 0000000000000000 00000000 Security Directory
Entry 5 0000000000000000 00000000 Base Relocation Directory [.reloc]
Entry 6 00000000000022cc 0000001c Debug Directory
Entry 7 0000000000000000 00000000 Description Directory
Entry 8 0000000000000000 00000000 Special Directory
Entry 9 0000000000000000 00000000 Thread Storage Directory [.tls]
Entry a 0000000000000000 00000000 Load Configuration Directory
Entry b 0000000000000000 00000000 Bound Import Directory
Entry c 0000000000000000 00000000 Import Address Table Directory
Entry d 0000000000000000 00000000 Delay Import Directory
Entry e 0000000000000000 00000000 CLR Runtime Header
Entry f 0000000000000000 00000000 Reserved

...                                                                          <----- .rsrc 리소스 디렉터리 섹션이 없다.

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00001fc0  0000000000000240  0000000000000240  00000240  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         000001c0  0000000000002200  0000000000002200  00002200  2**4   <---- 여기에도 .rsrc 섹션이 없다.
                  CONTENTS, ALLOC, LOAD, DATA
SYMBOL TABLE:
no symbols
```

이 버전의 애플리케이션을 실행하려고 하면 아래와 같이 실행된다.

```
FS0:\> HIIStringsUNIRC.efi
Error! Can't open EFI_HII_PACKAGE_LIST_PROTOCOL
```
