【问题标题】:How to set PE header Characteristics flags如何设置 PE 标头特征标志
【发布时间】:2021-07-12 05:39:46
【问题描述】:

我正在尝试使用 gcc 在 64 位模式下为 UEFI 制作 UEFI 可引导的 PE32+ 文件。

首先我正在编译源代码。

cc -nostartfiles -o bootx64.o bootx64.c

然后我将丢弃除 .text 和 .data 部分之外的所有内容并重命名文件。

objcopy -j .text -j .data --target=pei-x86-64 --subsystem=10 --strip-unneeded bootx64.o
mv bootx64.o bootx64.efi

这会生成一个几乎完美的文件。除了 PE 头特性标志设置错误。 (https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#characteristics)

我通过以下方式获得特征:

objdump -p bootx64.efi

输出是:

bootx64.efi:     file format pei-x86-64

Characteristics 0x20f
    relocations stripped
    executable
    line numbers stripped
    symbols stripped
    debugging information removed

所以特性标志设置为 0x020f 表示设置了这些标志:

0x0200 调试信息已从图像文件中删除。

0x0001 仅映像、Windows CE 和 Microsoft Windows NT 及更高版本。这表明该文件不包含基址重定位,因此必须在其首选基址处加载。如果基地址不可用,加载程序会报告错误。链接器的默认行为是从可执行 (EXE) 文件中去除基址重定位。

0x0002 仅图像。这表明镜像文件是有效的并且可以运行。如果未设置此标志,则表示链接器错误。

0x0004 COFF 行号已被删除。此标志已弃用,应为零。

0x0008 本地符号的 COFF 符号表条目已被删除。此标志已弃用,应为零。

在设置/重置和测试每个标志后,我发现 UEFI 不喜欢 0x0001 标志,因为这意味着它必须在首选地址加载代码。我的代码不包含任何重定位,但是当我手动删除 0x0001 标志时它工作正常。旁注:objcopy 还设置了两个不推荐使用的标志(0x0004 和 0x0008),我也希望不要发生这种情况。

objcopy 有什么方法可以将 PE 头特性标志设置为仅设置 0x0002,而 objcopy 直接取消设置 0x0001、0x0004 和 0x0008,而不是事后手动设置?

编辑: 我尝试将文件与 ld 链接,但存在一些问题。

ld --oformat pei-x86-64 --subsystem 10 -o bootx64.efi bootx64.o

结果

ld: unrecognized option '--subsystem'

尽管 GNU binutils 文档 (https://sourceware.org/binutils/docs/ld/Options.html) 说:

--哪个子系统

--subsystem which:major

--subsystem which:major.minor

指定您的程序将在哪个子系统下执行。这 其合法值为 native、windows、console、posix 和 xbox。 您也可以选择设置子系统版本。数值是 也接受了。 [此选项特定于 i386 PE 链接器的目标端口]

所以我需要一个选项“EFI”或数值为 10。此处不存在该选项。

我还尝试了 pe-x86-64 和 pei-i386 以及我发现的所有名称中带有 pe 的东西。没有子系统选项的链接会导致几乎完全为零的标题。

Objcopy 做得最好,但仍然不好(上面讨论的问题)我写了一个小实用程序,可以正确填写标题,所以最后我坚持使用我的实用程序。

【问题讨论】:

  • 从 binutils 2.34 开始,您可以告诉ld 始终创建重定位表(使用-Wl,--enable-reloc-section)。
  • 也许使用objcopy的--set-section-flags?
  • @ssbssa --enable-reloc-section 仅支持 i386pe 和 i386pep。不适用于 pei-x86-64 - 我如何将它与 pei-x86-64 一起使用?我在原始问题中添加了说明。谢谢!
  • @OlafDietsche 如何使用 --set-section-flags 设置 PE 标头特征标志?谢谢!
  • 我现在看到 ld 根本就没有被调用过,所以看起来您应该修改 objcopy 以完全满足您的需要。

标签: c gcc portable-executable uefi objcopy


【解决方案1】:

我认为您正在以一种不寻常的方式编译和链接(U}EFI 应用程序。我的经验是您构建了一个共享对象(DLL 或 .so),然后将其转换为 UEFI 可执行文件 (.efi) ,通常使用特殊工具。

例如,查看http://x86asm.net/articles/uefi-programming-first-steps/,一篇关于 EFI 工具包但仍然相关的旧文章。

cl /c /Zl /I"{EFI_Toolkit}\include\efi" /I"{EFI_Toolkit}\include\efi\em64t" hello.c
link /entry:main /dll /IGNORE:4086 hello.obj
fwimage app hello.dll hello.efi

fwimage 工作,但有些错误。

转向更现代的 EFI 构建环境,即 gnu-efi:

C         = gcc
AS         = as
LD         = ld.bfd
AR         = ar
RANLIB     = ranlib
OBJCOPY    = objcopy

ifeq ($(ARCH), x86_64)
  CFLAGS += -mno-red-zone
  LIBDIR := $(PREFIX)/$(ARCH)/lib
  LIBDIR += $(PREFIX)/$(ARCH)/gnuefi
  ifeq ($(HOSTARCH), ia32)
    ARCH3264 := -m64
  endif
endif

FORMAT    = efi-app-$(ARCH)
LDSCRIPT  = $(TOPDIR)/gnuefi/elf_$(ARCH)_efi.lds

CFLAGS     = $(ARCH3264) -g -O0 -fpic -Wall -fshort-wchar -fno-strict-aliasing -fno-merge-constants --std=gnu99 -D_GNU_SOURCE

.efi : %.so
        $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \
                -j .rela -j .reloc --target=$(FORMAT) $*.so $@

%.so: %.o
        $(LD) $(LDFLAGS) -o $@ $^ $(LIBS)

gnu-efi 并非没有问题和批评。例如,见https://dvdhrm.github.io/2019/01/31/goodbye-gnuefi/

最后一个例子,看看 EDKII 如何使用 genfw 构建 .efi:

GENFW = GenFw

CC_FLAGS = -g -Os -fshort-wchar -fno-builtin -fno-strict-aliasing -Wall -Werror -Wno-array-bounds -include AutoGen.h -fno-common -ffunction-sections -fdata-sections -DSTRING_ARRAY_NAME=$(BASE_NAME)Strings -Wno-parentheses-equality -Wno-tautological-compare -Wno-tautological-constant-out-of-range-compare -Wno-empty-body -Wno-unused-const-variable -Wno-varargs -Wno-unknown-warning-option -fno-stack-protector -mms-bitfields -Wno-address -Wno-shift-negative-value -Wno-unknown-pragmas -Wno-incompatible-library-redeclaration -fno-asynchronous-unwind-tables -mno-sse -mno-mmx -msoft-float -mno-implicit-float -ftrap-function=undefined_behavior_has_been_optimized_away_by_clang -funsigned-char -fno-ms-extensions -Wno-null-dereference -m64 "-DEFIAPI=__attribute__((ms_abi))" -mno-red-zone -mcmodel=small -fpie -Oz -flto -target x86_64-pc-linux-gnu
CC = clang

DLINK_FLAGS = -nostdlib -Wl,-n,-q,--gc-sections -z common-page-size=0x40 -Wl,--entry,$(IMAGE_ENTRY_POINT) -u $(IMAGE_ENTRY_POINT) -Wl,-Map,$(DEST_DIR_DEBUG)/$(BASE_NAME).map,--whole-archive -flto -Wl,-Oz -Wl,-melf_x86_64 -Wl,--oformat=elf64-x86-64 -Wl,-pie -mcmodel=small
DLINK = clang

$(OUTPUT_DIR)/DateTime.obj : $(MAKE_FILE)
$(OUTPUT_DIR)/DateTime.obj : $(DEBUG_DIR)/AutoGen.h
$(OUTPUT_DIR)/DateTime.obj : $(WORKSPACE)/MyApps/DateTime/DateTime.c
    "$(CC)" $(DEPS_FLAGS) $(CC_FLAGS) -c -o /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/OUTPUT/./DateTime.obj $(INC) /home/fpm/edk2/MyApps/DateTime/DateTime.c

$(OUTPUT_DIR)/DateTime.lib : $(OBJECT_FILES)
$(OUTPUT_DIR)/DateTime.lib : $(OBJECT_FILES_LIST)
    $(RM) /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/OUTPUT/DateTime.lib
    "$(SLINK)" cr /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/OUTPUT/DateTime.lib $(SLINK_FLAGS) @$(OBJECT_FILES_LIST)

$(DEBUG_DIR)/DateTime.dll : $(MAKE_FILE)
$(DEBUG_DIR)/DateTime.dll : $(STATIC_LIBRARY_FILES)
$(DEBUG_DIR)/DateTime.dll : $(STATIC_LIBRARY_FILES_LIST)
    "$(DLINK)" -o /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/DEBUG/DateTime.dll $(DLINK_FLAGS) -Wl,--start-group,@$(STATIC_LIBRARY_FILES_LIST),--end-group $(CC_FLAGS) $(DLINK2_FLAGS)
    "$(OBJCOPY)" $(OBJCOPY_FLAGS) /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/DEBUG/DateTime.dll

$(OUTPUT_DIR)/DateTime.efi : $(DEBUG_DIR)/DateTime.dll
    $(CP) /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/DEBUG/DateTime.dll $(DEBUG_DIR)/$(MODULE_NAME).debug
    $(OBJCOPY) --strip-unneeded -R .eh_frame /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/DEBUG/DateTime.dll
    -$(OBJCOPY) $(OBJCOPY_ADDDEBUGFLAG) /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/DEBUG/DateTime.dll
    -$(CP) $(DEBUG_DIR)/$(MODULE_NAME).debug $(BIN_DIR)/$(MODULE_NAME_GUID).debug
    "$(GENFW)" -e $(MODULE_TYPE) -o /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/OUTPUT/DateTime.efi /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/DEBUG/DateTime.dll $(GENFW_FLAGS)
    $(CP) /home/fpm/edk2/Build/MyApps/RELEASE_CLANG11/X64/MyApps/DateTime/DateTime/OUTPUT/DateTime.efi $(DEBUG_DIR)
    $(CP) 

genfw 工具在此处描述:https://edk2-docs.gitbook.io/edk-ii-basetools-user-guides/genfw

【讨论】:

  • 感谢您的回答!这很有帮助,因为我不知道大多数工具,我会研究它们!我确实使用了 gnu-efi,但我尽量避免它,因为它首先重新定位所有代码,然后每次你想调用一个函数时,你首先必须在 System V ABI 调用约定中调用一个包装函数,然后重新洗牌参数,然后在正确的 Microsoft ABI 中调用实际函数。这不是很优雅,您也不能在 gnu-efi 中自定义标头。我使用 __attribute__((ms_abi)) 所以它只是直接正确调用函数。
  • @colchy。恕我直言,gnu-efi 在 10 年前是一个有用的工具链,但自从添加了对交叉编译和其他架构的支持后就变得一团糟。
猜你喜欢
  • 2021-05-03
  • 1970-01-01
  • 1970-01-01
  • 2022-08-20
  • 2014-12-28
  • 2011-01-06
  • 2012-02-05
  • 2017-01-29
  • 1970-01-01
相关资源
最近更新 更多