# 42. GDB를 이용한 Driver/Application 및 OVMF Debug

GDB를 사용하여 디버깅을 수행하기 위해서는 올바른 오프셋에 디버그 심볼을 로그해야 한다.

* 메모리 상의 애플리케이션 오프셋
* 애플리케이션 내부의 오프셋

이번 과정에서는 이전에 개발한 `ShowBootVariable.efi` 애플리케이션을 디버깅한다.

## 메모리 상의 애플리케이션 오프셋 구하기

QEMU에서 OVMF를 실행한다.

```
qemu-system-x86_64 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -net none \
  -nographic \
  -global isa-debugcon.iobase=0x402 \
  -debugcon file:debug.log
```

또 다른 터미널을 이용하여 아래 명령어를 실행한다.

```
tail -f debug.log
```

이를 통해 런타임 로그를 추적할 수 있다.

부팅 이후 `ShowBootVariables.efi` 애플리케이션을 실행하고 아래와 같은 메세지를 `debug.log` 에서 확인한다.

```
Loading driver at 0x00006649000 EntryPoint=0x0000664C80F ShowBootVariables.efi
InstallProtocolInterface: BC62157E-3E33-4FEC-9920-2D3B36D750DF 666B898
ProtectUefiImageCommon - 0x666B4C0
  - 0x0000000006649000 - 0x0000000000005540
InstallProtocolInterface: 752F3136-4E16-4FDC-A22A-E5F46812F4CA 7EA36C8
```

다음 문장에서 제작한 이미지가 로드된 주소를 확인한다. `0x6649000`

```
Loading driver at 0x00006649000 EntryPoint=0x0000664C80F ShowBootVariables.efi
```

## Application section의 offset 구하기

`objdump` 명령어를 이용하여 `.efi` 파일에 포함된 섹션을 확인한다.

```
$ objdump -h Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.efi


Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.efi:     file format pei-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00004d40  0000000000000240  0000000000000240  00000240  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .data         00000580  0000000000004f80  0000000000004f80  00004f80  2**4
                  CONTENTS, ALLOC, LOAD, DATA
  2 .reloc        00000080  0000000000005500  0000000000005500  00005500  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

```

또한 `.debug` 파일에 존재하는 섹션도 확인한다.

```
$ objdump -h Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.debug

Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.debug:     file format elf64-x86-64

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00004d15  0000000000000240  0000000000000240  000000c0  2**6
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         000004a1  0000000000004f80  0000000000004f80  00004e00  2**6
                  CONTENTS, ALLOC, LOAD, RELOC, DATA
  2 .eh_frame     00000000  0000000000005440  0000000000005440  000052c0  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .rela         00000510  0000000000005440  0000000000005440  000052c0  2**3
                  CONTENTS, READONLY
  4 .build-id     00000024  0000000000005950  0000000000005950  000057d0  2**2
                  CONTENTS, READONLY
  5 .debug_info   00026067  0000000000000000  0000000000000000  000057f4  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
  6 .debug_abbrev 00003553  0000000000000000  0000000000000000  0002b85b  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
  7 .debug_loc    00009fde  0000000000000000  0000000000000000  0002edae  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
  8 .debug_aranges 00000570  0000000000000000  0000000000000000  00038d8c  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
  9 .debug_ranges 000013f0  0000000000000000  0000000000000000  000392fc  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
 10 .debug_line   00008163  0000000000000000  0000000000000000  0003a6ec  2**0
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
 11 .debug_str    000065d7  0000000000000000  0000000000000000  0004284f  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 12 .debug_frame  000015a8  0000000000000000  0000000000000000  00048e28  2**3
                  CONTENTS, RELOC, READONLY, DEBUGGING, OCTETS
```

두 정보의 차이는 `.debug` 정보의 유무이다.

`objdump` 결과를 통해서 필요한 섹션의 오프셋을 확인할 수 있다. (환경에 따라 그 결과가 다를 수도 있다.)

* `.text` -> `0x240`
* `.data` -> `0x4f40`

## GDB에 디버그 심볼적용

QEMU 실행 시 `-s` 옵션을 포함하여 원격 디버깅을 활성화 한다. 해당 옵션은 `tcp::1234` 의 축어이며 QEMU는 해당 포트에서 GDB 연결을 수신할 수 있다.

```
qemu-system-x86_64 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -net none \
  -nographic \
  -s
```

심볼 적용을 위해 사전에 확인한 애플리케이션 이미지 주소에 구한 `.text` 와 `.data` 의 오프셋 값 연산한다.

```
0x6649000 +  0x240 = 0x6649240
0x6649000 + 0x4f40 = 0x664df40
```

GDB를 실행하고 다음과 같이 심볼을 적용한다.

```
$ gdb
(gdb) add-symbol-file Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.debug 0x65FD240 -s .data 0x6601F80
add symbol table from file "Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.debug" at
	.text_addr = 0x65fd240
	.data_addr = 0x6601f80
(y or n) y
Reading symbols from Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.debug...
```

애플리케이션의 Entry Point에 break point을 적용하기 위해서 `ShellAppMain` 함수를 검색한다.

```
$ grep -n ShellAppMain UefiLessonsPkg/ShowBootVariables/ShowBootVariables.c
63:INTN EFIAPI ShellAppMain(IN UINTN Argc, IN CHAR16 **Argv)
```

이후 진입점에 break point를 설정한다.

```
(gdb) b UefiLessonsPkg/ShowBootVariables/ShowBootVariables.c:63
Breakpoint 1 at 0x664c50f: file /home/kostr/tiano/edk2/UefiLessonsPkg/ShowBootVariables/ShowBootVariables.c, line 68.
```

원격 디버깅을 실행한다.

```
(gdb) target remote :1234
Remote debugging using :1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x000000000716e151 in ?? ()
```

연결 이후 QEMU는 중단되며 재실행을 위해서는 GDB에서 `c` 명령어를 입력하면 된다.

```
(gdb) c
Continuing.
```

설정한 break point를 trigger 하기 위해 애플리케이션을 실행한다.

```
FS0:\> ShowBootVariables.efi
```

```
Breakpoint 1, ShellAppMain (Argc=1, Argv=0x6609b98)
    at /home/minishell/src/edk2-ws/edk2/UefiLessonsPkg/ShowBootVariables/ShowBootVariables.c:69
69	  Status = GetNvramVariable(L"BootCurrent", &gEfiGlobalVariableGuid, (VOID**)&BootCurrent, &OptionSize);
```

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fgit-blob-9ce4a9f1844aeead4bf0744e54a9de60c034e31c%2Fimage%20(1)%20(3).png?alt=media" alt=""><figcaption></figcaption></figure>

GDB에 편의를 위한 TUI 기능이 존재한다.

```
(gdb) tui enable
```

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fgit-blob-5faecff5c9603578de56a6ea12f733e19815d872%2Fimage%20(4)%20(1).png?alt=media" alt=""><figcaption></figcaption></figure>

## efi.py

앞선 과정들을 효율적으로 수행하기 위해서 Artem Nefedov 의 [efi.py](https://github.com/Kostr/UEFI-Lessons/blob/master/Lessons/Lesson_42/efi.py) 스크립트를 사용한다.

해당 스크립트를 통해 디버그 심볼을 로드하는데 큰 도움을 받을 수 있다.\
<https://github.com/artem-nefedov/uefi-gdb/blob/master/efi.py>

```bash
$ gdb -ex 'source efi.py'
```

gdb 내부에서 추가된 `efi` 명령어를 이용해 디버그 심볼을 로드한다.

```
(gdb) efi -64 ShowBootVariables
Turning pagination off
Using pre-defined driver list: ['ShowBootVariables']
The target architecture is assumed to be i386:x86-64:intel
With architecture X64
Looking for addresses in debug.log
EFI file Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.efi
 Base address 0x000065FD000
.text address 0x0000000000000240
.data address 0x0000000000004f80
add symbol table from file "Build/UefiLessonsPkg/RELEASE_GCC5/X64/ShowBootVariables.debug" at
	.text_addr = 0x65fd240
	.data_addr = 0x6601f80
Restoring pagination

```

위 과정은 스크립트가 `ShowBootVariables` 문자열에 대해 `debug.log` 파일에서 검색하고 주소 정보를 가져온 다음 필요한 심볼을 구해 gdb에 로드한다.

디버깅을 시도해보면 앞선 과정과 동일한 결과를 얻을 수 있다.

## run\_gdb.sh

`efi.py` 도 훌륭한 스크립트이지만 아직 수행할 작업이 많다.

* 적절한 인자로 QEMU를 실행하고 부팅 이후 애플리케이션을 실행해야 하며, 애플리케이션을 다시 컴파일 할 경우 이 과정을 매번 수행해야 한다.
* `efi.py` 를 GDB에 로드한 뒤 실행하여 `efi` 명령을 실행해야 한다.
* break point 지정을 위해 함수 Entry Point를 검색해야 한다.
* QEMU 환경을 디버깅하기 위해 별도의 터미널이 요구된다.
* GDB를 QEMU에 연결하고 애플리케이션을 실행해야 한다.

따라서 디버깅과 동시에 OVMF 로그를 볼 수 있으며 위 과정을 자동화할 수 있는 스크립트를 작성하자.\
<https://github.com/Kostr/UEFI-Lessons/blob/master/scripts/run_gdb.sh>

```bash
#!/bin/bash
##
# Copyright (c) 2021, Konstantin Aladyshev <aladyshev22@gmail.com>
#
# SPDX-License-Identifier: MIT
##

##### Controllable parameters #####
QEMU_SHARED_FOLDER=~/UEFI_disk
PACKAGE=UefiLessonsPkg
###################################

function show_help {
  echo "Description:"
  echo "  run_gdb.sh is a script that helps to debug UEFI shell applications and drivers"
  echo ""
  echo "Usage: run_gdb.sh -m <module> [-1|-f|-p <package>|-q <dir>]"
  echo "  -1            This is a first run of this configuration"
  echo "                (in this case before main gdb launch there would be another QEMU start that will create 'debug.log' file)"
  echo "  -f            Load all debug symbols"
  echo "                (this will load all OVMF debug symbols - with this you could step inside OVMF functions)"
  echo "  -m <module>   UEFI module to debug"
  echo "  -p <package>  UEFI package to debug"
  echo "                (by default it is equal to PACKAGE variable in the head of the script)"
  echo "  -q <dir>      QEMU shared directory"
  echo "                (by default it is equal to QEMU_SHARED_FOLDER variable in the head of the script)"
  echo ""
  echo "Examples:"
  echo " run_gdb.sh -1 -m MyApp      - create 'debug.log' file with the necessary address information for the 'MyApp'"
  echo "                               and debug it with gdb"
  echo " run_gdb.sh -1 -m MyApp -f   - create 'debug.log' file with the necessary address information for the 'MyApp'"
  echo "                               and debug it with gdb (all debug symbols are included, i.e. you can step into OVMF functions)"
  echo " run_gdb.sh -m MyApp         - debug 'MyApp' with gdb ('debug.log' was created in the last run, no need to remake it again)"
}


# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

while getopts "h?1fm:q:p:" opt; do
  case "$opt" in
    h|\?)
      show_help
      exit 0
      ;;
    1)  FIRST_RUN=1
      ;;
    f)  FULL=1
      ;;
    m)  TARGET=$OPTARG
      ;;
    q)  QEMU_SHARED_FOLDER=$OPTARG
      ;;
    p)  PACKAGE=$OPTARG
      ;;
  esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift



if [[ ! -z "${FULL}" ]]; then
  DRIVERS=''
else
  DRIVERS=${TARGET}
fi

if [[ -z $TARGET ]]; then
  echo "Error! Module is not provided."
  echo ""
  show_help
  exit 1
fi

function test_file {
  FILE_NAME=$1
  if [[ ! -f ${FILE_NAME} ]]; then
    echo "Error! There is no file ${FILE_NAME}"
    exit 1;
  fi
}

TARGET_INF="${PACKAGE}/${TARGET}/${TARGET}.inf"
TARGET_C="${PACKAGE}/${TARGET}/${TARGET}.c"

OVMF="Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd"

test_file "${TARGET_INF}"
test_file "${TARGET_C}"
test_file "${OVMF}"

ENTRY_POINT_NAME=$(grep ENTRY_POINT ${TARGET_INF} | cut -f 2 -d "=")
if [ ${ENTRY_POINT_NAME} == "ShellCEntryLib" ]; then
  ENTRY_POINT_NAME="ShellAppMain"
fi
ENTRY_POINT_LINE=$(grep -n ${ENTRY_POINT_NAME} ${TARGET_C} | cut -f 1 -d ":")

MODULE_TYPE=$(grep MODULE_TYPE ${TARGET_INF} | cut -f 2 -d "=")
if [ ${MODULE_TYPE} == "UEFI_DRIVER" ]; then
  LAUNCH_COMMAND="load fs0:${TARGET}.efi"
else
  LAUNCH_COMMAND="fs0:${TARGET}.efi"
fi

if [[ ! -z "${FIRST_RUN}" || ! -f debug.log ]]; then
  touch debug.log
  # If it is a first run, we need to create 'debug.log' file for addresses
  TARGET_EFI="Build/${PACKAGE}/RELEASE_GCC5/X64/${TARGET}.efi"
  test_file "${TARGET_EFI}"
  cp ${TARGET_EFI} ${QEMU_SHARED_FOLDER}
  tmux new-session \; \
   send-keys "tail -f debug.log" Enter \; \
   split-window -v \; \
   send-keys "qemu-system-x86_64 \
             -drive if=pflash,format=raw,readonly,file=${OVMF} \
             -drive format=raw,file=fat:rw:${QEMU_SHARED_FOLDER} \
             -net none \
             -nographic \
             -global isa-debugcon.iobase=0x402 \
             -debugcon file:debug.log \
             -s" C-m Enter \; \
   send-keys C-m Enter \; \
   send-keys "${LAUNCH_COMMAND}" Enter \;
fi


test_file "${QEMU_SHARED_FOLDER}/${TARGET}.efi"
touch debug_temp.log
tmux new-session \; \
 send-keys "gdb -ex 'source efi.py' -tui" Enter \; \
 split-window -h \; \
 
 send-keys "tail -f debug_temp.log" Enter \; \
 split-window -v \; \
 send-keys "qemu-system-x86_64 \
            -drive if=pflash,format=raw,readonly,file=${OVMF} \
            -drive format=raw,file=fat:rw:${QEMU_SHARED_FOLDER} \
            -net none \
            -nographic \
            -global isa-debugcon.iobase=0x402 \
            -debugcon file:debug_temp.log \
            -s" C-m Enter \; \
 select-pane -t 0 \; \
 send-keys "efi -64 ${DRIVERS}" Enter \; \
 send-keys "b ${TARGET_C}:${ENTRY_POINT_LINE}" Enter \; \
 send-keys Enter \; \
 send-keys "target remote :1234" Enter \; \
 send-keys "c" Enter \; \
 select-pane -t 2 \; \
 send-keys C-m Enter \; \
 send-keys "${LAUNCH_COMMAND}" Enter \; wnd
```

위 코드는 `tmux` 세션을 이용하여 필요한 키를 자동적으로 전달할 수 있다.

```
$ ./run_gdb.sh -h
Description:
  run_gdb.sh is a script that helps to debug UEFI shell applications and drivers

Usage: run_gdb.sh -m <module> [-1|-f|-p <package>|-q <dir>]
  -1            This is a first run of this configuration
                (in this case before main gdb launch there would be another QEMU start that will create 'debug.log' file)
  -f            Load all debug symbols
                (this will load all OVMF debug symbols - with this you could step inside OVMF functions)
  -m <module>   UEFI module to debug
  -p <package>  UEFI package to debug
                (by default it is equal to PACKAGE variable in the head of the script)
  -q <dir>      QEMU shared directory
                (by default it is equal to QEMU_SHARED_FOLDER variable in the head of the script)

Examples:
 run_gdb.sh -1 -m MyApp      - create 'debug.log' file with the necessary address information for the 'MyApp'
                               and debug it with gdb
 run_gdb.sh -1 -m MyApp -f   - create 'debug.log' file with the necessary address information for the 'MyApp'
                               and debug it with gdb (all debug symbols are included, i.e. you can step into OVMF functions)
 run_gdb.sh -m MyApp         - debug 'MyApp' with gdb ('debug.log' was created in the last run, no need to remake it again)
```

처음 디버깅하는 경우 `debug.log` 를 생성해야 한다.

```
./run_gdb.sh -1 -m ShowBootVariables
```

이후 QEMU 환경에서 `enter`만 입력해도 대상 애플리케이션이 실행된다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fgit-blob-ed33654a2c3085c3568286bc14ee60d64c7de0f5%2Fimage%20(2)%20(2).png?alt=media" alt=""><figcaption></figcaption></figure>

이후 다른 터미널을 통해 tmux 세션을 닫거나 tmux 환경에서 `ctrl+b` 입력하고 `:kill-session` 을 입력하여 tmux 세션을 닫는다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fgit-blob-ba3628bb9c9c1895bc2d3f9774083d2ab90522c1%2Fimage%20(4).png?alt=media" alt=""><figcaption></figcaption></figure>

다시 로드된 QEMU 환경과 디버그 심볼이 추가된 GDB 환경을 확인할 수 있다. QEMU 환경에서 `enter` 를 입력하면 대상 애플리케이션 Entry Point에break point가 발생한다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fgit-blob-958ebee5ff991040c7fd1587002f4a7d6f9a3b66%2Fimage%20(20).png?alt=media" alt=""><figcaption></figcaption></figure>

## 모든 symbols 로드하기

만약 애플리케이션 또는 드라이버 외부에서 정의된 함수 (예: gRT->GetVariable)를 단계별로 실행하려면 모든 심볼을 GDB에 로드해야 한다.

이 때 `-f` 인자를 통해 `run_gdb.sh` 를 실행하면 된다.

```
./run_gdb.sh -1 -m ShowBootVariables -f
```

함수들의 심볼이 존재하므로 원하는 지점에 break point를 설정할 수 있다.

## OVMF 자체 Debug

OVMF 자체를 디버깅할 경우에 시작 시 QEMU를 중지해야 한다. 이 작업을 위해 `-S` 옵션을 사용할 수 있다.

```
qemu-system-x86_64 \
  -drive if=pflash,format=raw,readonly,file=Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd \
  -drive format=raw,file=fat:rw:~/UEFI_disk \
  -net none \
  -nographic \
  -s \
  -S
```

이후 GDB을 실행하고 이전과 같이 `debug.log` 파일을 통해 필요 symbols를 로드해야 한다.

아래는 해당 작업을 자동화하기 위한 `run_gdb_ovmf.sh` 스크립트이다.\
<https://github.com/Kostr/UEFI-Lessons/blob/master/scripts/run_gdb_ovmf.sh>

```bash
#!/bin/bash
##
# Copyright (c) 2021, Konstantin Aladyshev <aladyshev22@gmail.com>
#
# SPDX-License-Identifier: MIT
##

##### Controllable parameters #####
QEMU_SHARED_FOLDER=~/UEFI_disk
###################################

function show_help {
  echo "Description:"
  echo "  run_gdb_ovmf.sh is a script that helps to debug OVMF"
  echo ""
  echo "Usage: run_gdb_ovmf.sh [-1] [-q <dir>]"
  echo "  -1            This is a first run of this configuration"
  echo "                (in this case before main gdb launch there would be another QEMU start that will create 'debug.log' file)"
  echo "  -q <dir>      QEMU shared directory"
  echo "                (by default it is equal to QEMU_SHARED_FOLDER variable in the head of the script)"
  echo ""
  echo "Examples:"
  echo " run_gdb_ovmf.sh -1      - create 'debug.log' file with the necessary address information"
  echo "                           and debug OVMF it with gdb"
  echo " run_gdb_ovmf.sh         - debug OVMF with gdb ('debug.log' was created in the last run, no need to remake it again)"
}


# A POSIX variable
OPTIND=1         # Reset in case getopts has been used previously in the shell.

while getopts "h?1q:" opt; do
  case "$opt" in
    h|\?)
      show_help
      exit 0
      ;;
    1)  FIRST_RUN=1
      ;;
    q)  QEMU_SHARED_FOLDER=$OPTARG
      ;;
  esac
done

shift $((OPTIND-1))

[ "${1:-}" = "--" ] && shift



function test_file {
  FILE_NAME=$1
  if [[ ! -f ${FILE_NAME} ]]; then
    echo "Error! There is no file ${FILE_NAME}"
    exit 1;
  fi
}

OVMF="Build/OvmfX64/DEBUG_GCC5/FV/OVMF.fd"

test_file "${OVMF}"


if [[ ! -z "${FIRST_RUN}" || ! -f debug.log ]]; then
  touch debug.log
  # If it is a first run, we need to create 'debug.log' file for addresses
  tmux new-session \; \
   send-keys "tail -f debug.log" Enter \; \
   split-window -v \; \
   send-keys "qemu-system-x86_64 \
             -drive if=pflash,format=raw,readonly,file=${OVMF} \
             -drive format=raw,file=fat:rw:${QEMU_SHARED_FOLDER} \
             -net none \
             -nographic \
             -global isa-debugcon.iobase=0x402 \
             -debugcon file:debug.log \
             -s" C-m Enter \;
fi

touch debug_temp.log
tmux new-session \; \
 send-keys "gdb -ex 'source efi.py' -tui" Enter \; \
 split-window -h \; \
 send-keys "tail -f debug_temp.log" Enter \; \
 split-window -v \; \
 send-keys "qemu-system-x86_64 \
            -drive if=pflash,format=raw,readonly,file=${OVMF} \
            -drive format=raw,file=fat:rw:${QEMU_SHARED_FOLDER} \
            -net none \
            -nographic \
            -global isa-debugcon.iobase=0x402 \
            -debugcon file:debug_temp.log \
            -s -S" C-m Enter \; \
 select-pane -t 0 \; \
 send-keys "efi -64" Enter \; \
 send-keys "target remote :1234" Enter \;

```

```
$ ./run_gdb_ovmf.sh -h
Description:
  run_gdb_ovmf.sh is a script that helps to debug OVMF

Usage: run_gdb_ovmf.sh [-1] [-q <dir>]
  -1            This is a first run of this configuration
                (in this case before main gdb launch there would be another QEMU start that will create 'debug.log' file)
  -q <dir>      QEMU shared directory
                (by default it is equal to QEMU_SHARED_FOLDER variable in the head of the script)

Examples:
 run_gdb_ovmf.sh -1      - create 'debug.log' file with the necessary address information
                           and debug OVMF it with gdb
 run_gdb_ovmf.sh         - debug OVMF with gdb ('debug.log' was created in the last run, no need to remake it again)
```

`run_gdb.sh` 와 별반 다르지 않지만 break point을 설정하거나 OVMF를 실행하지 않는다는 차이점이 존재한다.

<figure><img src="https://224378457-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FgAFiQ5sYs9AjXHJxMFpe%2Fuploads%2Fgit-blob-67a76d9310e5b01a21843261f2bddc819e0eec91%2Fimage%20(1)%20(4).png?alt=media" alt=""><figcaption></figcaption></figure>

## GDB cheatsheet

아래는 GDB 환경에서 사용할 수 있는 명령어 일부의 정보이다.

* `s` - step
* `n` - next
* `fin` - step out
* `i loc` - info about local variables
* `i arg` - info about function arguments
* `p <var>` - print value of `<var>`
* `b <number>` - set breakpoint at line `<number>` in the current file
* `b <file>:<number>` - set breakpoint at line `<number>` in the `<file>` file
* `b <func>` - break on function `<func>`
* `i b` - info breakpoints
* `d <number>` - delete breakpoint `<number>`
* `c` - continue
* `q` - quit GDB
* `Ctrl+p` - previous GDB command in history
* `Ctrl+n` - next GDB command in history
* `Ctrl+x` and `o` - change active window in tui mode

16 진수로 값을 출력하고 싶을 경우에는 `p/x` 를 사용한다.

CHAR16 문자열을 출력할 경우 `x /sh` 명령어를 이용할 수 있다. 다음은 장치 경로를 출력하는 예제이다.

```
(gdb) p ConvertDevicePathToText(DevicePath, 0, 1)
$1 = (CHAR16 *) 0x6d04518
(gdb) x /sh 0x6d04518 
0x6d04518:      u"PciRoot(0x0)/Pci(0x2,0x0)"
```

또는

```
(gdb) x /sh ConvertDevicePathToText(DevicePath, 0, 1)
0x6d02098:      u"PciRoot(0x0)/Pci(0x2,0x0)"
```

만약 출력 데이터의 길이가 길어 완전히 출력하지 못할 경우 다음 명령어를 통해 출력 길이 제한을 해제할 수 있다.

```
(gdb) set print elements 0
```

## TMUX cheatsheet

* `Ctrl+b` and `up/down/left/right` - switch between panes
* `Ctrl+b` and `:kill-session` - close all panes
* `Ctrl+b` and `Ctrl+up/down/left/right` - change pane size
