From 0e7ba5947eb38b79de2051ecf3b95055e620475c Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Sat, 20 Sep 2014 14:03:03 -0400 Subject: [PATCH 60/74] Generate a sane PE header on shim, fallback, and MokManager. It turns out a7249a65 was masking a second problem - on some binaries, when we actually don't have any base relocations at all, binutils' "objcopy --target efi-app-x86_64" is generating a PE header with a base relocations pointer that happily points into the middle of our text section. So with shim processing base relocations correctly, it refuses to load those binaries. For example, on one binary I just built: 00000130 00 a0 00 00 0a 00 00 00 00 00 00 00 00 00 00 00 |................| which says there's a Base Relocation Table at 0xa000 that's 0xa bytes long. That's here: 0000a000 58 00 29 00 00 00 00 00 48 00 44 00 28 00 50 00 |X.).....H.D.(.P.| 0000a010 61 00 72 00 74 00 25 00 64 00 2c 00 53 00 69 00 |a.r.t.%.d.,.S.i.| 0000a020 67 00 25 00 67 00 29 00 00 00 00 00 00 00 00 00 |g.%.g.).........| 0000a030 48 00 44 00 28 00 50 00 61 00 72 00 74 00 25 00 |H.D.(.P.a.r.t.%.| So the table is: 0000a000 58 00 29 00 00 00 00 00 48 00 |X.).....H. | That wouldn't be so bad, except those binaries are MokManager.efi, fallback.efi, and shim.efi, and sometimes they're .reloc, which we're actually trying to handle correctly now because grub builds with a real and valid .reloc table. So though I didn't think there was any hair left on this yak, more shaving ensues. With this change, instead of letting objcopy do whatever it likes, we switch to "-O binary" and merely link in a header that's appropriate for our binaries. This is the same method Ard wrote for aarch64, and it seems to work fine in either place (modulo some minor changes.) At some point this should be merged into gnu-efi instead of carrying our own crt0-efi-x86_64.S, but that's a less immediate problem. I did not need this problem. Signed-off-by: Peter Jones --- Makefile | 24 ++++++-- crt0-efi-x86_64.S | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++ elf_x86_64_efi.lds | 85 +++++++++++++------------ 3 files changed, 236 insertions(+), 50 deletions(-) create mode 100644 crt0-efi-x86_64.S diff --git a/Makefile b/Makefile index 5bc513c..d5fd55b 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,10 @@ EFI_PATH := /usr/lib64/gnuefi LIB_GCC = $(shell $(CC) -print-libgcc-file-name) EFI_LIBS = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a --end-group $(LIB_GCC) -EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o +ifeq ($(ARCH),x86_64) +EFI_CRT_OBJS := crt0-efi-$(ARCH).o +endif +EFI_CRT_OBJS ?= $(EFI_PATH)/crt0-efi-$(ARCH).o EFI_LDS = elf_$(ARCH)_efi.lds DEFAULT_LOADER := \\\\grub.efi @@ -52,11 +55,11 @@ ifneq ($(origin VENDOR_DBX_FILE), undefined) CFLAGS += -DVENDOR_DBX_FILE=\"$(VENDOR_DBX_FILE)\" endif -LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) +LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL VERSION = 0.7 -TARGET = shim.efi MokManager.efi.signed fallback.efi.signed +TARGET += shim.efi MokManager.efi.signed fallback.efi.signed OBJS = shim.o netboot.o cert.o replacements.o version.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key shim.cer SOURCES = shim.c shim.h netboot.c include/PeImage.h include/wincert.h include/console.h replacements.c replacements.h version.c version.h @@ -94,17 +97,17 @@ shim.o: $(SOURCES) shim_cert.h cert.o : cert.S $(CC) $(CFLAGS) -c -o $@ $< -shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a +shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a $(EFI_CRT_OBJS) $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) fallback.o: $(FALLBACK_SRCS) -fallback.so: $(FALLBACK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a +fallback.so: $(FALLBACK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a $(EFI_CRT_OBJS) $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) MokManager.o: $(MOK_SOURCES) -MokManager.so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a +MokManager.so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a lib/lib.a $(EFI_CRT_OBJS) $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) lib/lib.a Cryptlib/libcryptlib.a: @@ -128,8 +131,17 @@ SUBSYSTEM := 0xa LDFLAGS += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM) endif +ifeq ($(ARCH),x86_64) +FORMAT := -O binary +SUBSYSTEM := 0xa +LDFLAGS += --defsym=EFI_SUBSYSTEM=$(SUBSYSTEM) +endif + FORMAT ?= --target efi-app-$(ARCH) +crt0-efi-x86_64.o : crt0-efi-x86_64.S + $(CC) $(CFLAGS) -DEFI_SUBSYSTEM=$(SUBSYSTEM) -c -o $@ $< + %.efi: %.so $(OBJCOPY) -j .text -j .sdata -j .data \ -j .dynamic -j .dynsym -j .rel* \ diff --git a/crt0-efi-x86_64.S b/crt0-efi-x86_64.S new file mode 100644 index 0000000..f334a63 --- /dev/null +++ b/crt0-efi-x86_64.S @@ -0,0 +1,177 @@ +/* crt0-efi-x86_64.S - x86_64 EFI startup code. + * + * Copyright 2014 Red Hat, Inc. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + .section .text.head + + /* + * Magic "MZ" signature for PE/COFF + */ + .globl ImageBase +ImageBase: + .ascii "MZ" + .skip 58 // 'MZ' + pad + offset == 64 + .long pe_header - ImageBase // Offset to the PE header. + .long 0x0eba1f0e /* terrifying code */ + .long 0xcd09b400 /* terrifying code */ + .long 0x4c01b821 /* terrifying code */ + .short 0x21cd /* terrfiying code */ + .ascii "The only winning move is not to play.\r\r\n$" /* DOS text */ + .skip 9 +pe_header: + .ascii "PE" + .short 0 +coff_header: + .short 0x8664 // x86_64 + .short 1 // nr_sections + .long 0 // TimeDateStamp + .long 0 // PointerToSymbolTable + .long 0 // NumberOfSymbols + .short section_table - optional_header // SizeOfOptionalHeader + .short 0x206 // Characteristics. + // IMAGE_FILE_DEBUG_STRIPPED | + // IMAGE_FILE_EXECUTABLE_IMAGE | + // IMAGE_FILE_LINE_NUMS_STRIPPED +optional_header: + .short 0x20b // PE32+ format + .byte 0x02 // MajorLinkerVersion + .byte 0x18 // MinorLinkerVersion + .long _edata - _start // SizeOfCode + .long 0 // SizeOfInitializedData + .long 0 // SizeOfUninitializedData + .long _start - ImageBase // AddressOfEntryPoint + .long _start - ImageBase // BaseOfCode + +extra_header_fields: + .quad 0 // ImageBase + .long 0x20 // SectionAlignment + .long 0x8 // FileAlignment + .short 0 // MajorOperatingSystemVersion + .short 0 // MinorOperatingSystemVersion + .short 0 // MajorImageVersion + .short 0 // MinorImageVersion + .short 0 // MajorSubsystemVersion + .short 0 // MinorSubsystemVersion + .long 0 // Win32VersionValue + + .long _edata - ImageBase // SizeOfImage + + // Everything before the kernel image is considered part of the header + .long _start - ImageBase // SizeOfHeaders + .long 0 // CheckSum + .short EFI_SUBSYSTEM // Subsystem + .short 0 // DllCharacteristics + .quad 0 // SizeOfStackReserve + .quad 0 // SizeOfStackCommit + .quad 0 // SizeOfHeapReserve + .quad 0 // SizeOfHeapCommit + .long 0 // LoaderFlags + .long 0x10 // NumberOfRvaAndSizes + + .quad 0 // ExportTable + .quad 0 // ImportTable + .quad 0 // ResourceTable + .quad 0 // ExceptionTable + .quad 0 // CertificationTable + .quad 0 // BaseRelocationTable + .quad 0 // DebugTable + .quad 0 // ArchTable + .quad 0 // GlobalPointerTable + .quad 0 // .tls + .quad 0 // LoadConfigTable + .quad 0 // BoundImportsTable + .quad 0 // ImportAddressTable + .quad 0 // DelayLoadImportTable + .quad 0 // ClrRuntimeHeader (.cor) + .quad 0 // Reserved + + // Section table +section_table: + .ascii ".text" + .byte 0 + .byte 0 + .byte 0 // end of 0 padding of section name + + .long _edata - _start // VirtualSize + .long _start - ImageBase // VirtualAddress + .long _edata - _start // SizeOfRawData + .long _start - ImageBase // PointerToRawData + .long 0 // PointerToRelocations (0 for executables) + .long 0 // PointerToLineNumbers (0 for executables) + .short 0 // NumberOfRelocations (0 for executables) + .short 0 // NumberOfLineNumbers (0 for executables) + .long 0x60500020 // Characteristics (section flags) + + /* + * The EFI application loader requires a relocation section + * because EFI applications must be relocatable. This is a + * dummy section as far as we are concerned. + */ + .ascii ".reloc" + .byte 0 + .byte 0 // end of 0 padding of section name + + .long 0 // VirtualSize + .long 0 // VirtualAddress + .long 0 // SizeOfRawData + .long 0 // PointerToRawData + .long 0 // PointerToRelocations + .long 0 // PointerToLineNumbers + .short 0 // NumberOfRelocations + .short 0 // NumberOfLineNumbers + .long 0x42100040 // Characteristics (section flags) + + /* x86-64 needs this padding here; without it, some machines simply + * refuse to admit this is an EFI binary. I'm not really sure why; + * reading the spec, it's unclear, but you'd expect it would need to + * be aligned to (1 << FileAlignment), which would mean not having + * the spacing. + */ + .quad 0 +_start: + subq $8, %rsp + pushq %rcx + pushq %rdx + +0: + lea ImageBase(%rip), %rdi + lea _DYNAMIC(%rip), %rsi + + popq %rcx + popq %rdx + pushq %rcx + pushq %rdx + call _relocate + + popq %rdi + popq %rsi + + call efi_main + addq $8, %rsp + +.exit: + ret diff --git a/elf_x86_64_efi.lds b/elf_x86_64_efi.lds index f981102..091187b 100644 --- a/elf_x86_64_efi.lds +++ b/elf_x86_64_efi.lds @@ -4,63 +4,60 @@ OUTPUT_ARCH(i386:x86-64) ENTRY(_start) SECTIONS { - . = 0; - ImageBase = .; - .hash : { *(.hash) } /* this MUST come first! */ - . = ALIGN(4096); - .eh_frame : - { - *(.eh_frame) - } - . = ALIGN(4096); - .text : - { - *(.text) - } - . = ALIGN(4096); - .reloc : - { - *(.reloc) + .text 0x0 : { + *(.text.head) + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + *(.srodata) + *(.rodata*) + . = ALIGN(16); + _etext = .; } - . = ALIGN(4096); + .dynamic : { *(.dynamic) } .data : { - *(.rodata*) - *(.got.plt) - *(.got) - *(.data*) - *(.sdata) - /* the EFI loader doesn't seem to like a .bss section, so we stick - it all into .data: */ - *(.sbss) - *(.scommon) - *(.dynbss) - *(.bss) - *(COMMON) - *(.rel.local) + *(.sdata) + *(.data) + *(.data1) + *(.data.*) + *(.got.plt) + *(.got) + + /* the EFI loader doesn't seem to like a .bss section, so we stick + * it all into .data: */ + . = ALIGN(16); + _bss = .; + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + . = ALIGN(16); + _bss_end = .; } . = ALIGN(4096); .vendor_cert : { - *(.vendor_cert) + *(.vendor_cert) } + . = ALIGN(4096); - .dynamic : { *(.dynamic) } - . = ALIGN(4096); - .rela : - { - *(.rela.data*) - *(.rela.got) - *(.rela.stab) - } + .rela.dyn : { *(.rela.dyn) } + .rela.plt : { *(.rela.plt) } + .rela.got : { *(.rela.got) } + .rela.data : { *(.rela.data) *(.rela.data*) } + _edata = .; + _data_size = . - _etext; + . = ALIGN(4096); - .dynsym : { *(.dynsym) } + .dynsym : { *(.dynsym) } . = ALIGN(4096); - .dynstr : { *(.dynstr) } + .dynstr : { *(.dynstr) } . = ALIGN(4096); - .ignored.reloc : + /DISCARD/ : { - *(.rela.reloc) + *(.rel.reloc) *(.eh_frame) *(.note.GNU-stack) } -- 1.9.3