# 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
```


---

# Agent Instructions: 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/hii/50.uefi_hii_resource_section.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.
