[efi] Place debug directory within .rodata section

Some strict PE parsers will refuse a debug directory that lies within
the file headers (rather than within a defined section), and will fail
to load the binary.

Place the debug directory at the start of the .rodata section for
iPXE, and at the start of the .data section for wimboot.

The debug directory irritatingly includes an RVA and a file offset,
neither of which can be calculated by the linker.  Update these fields
at the time of running elf2efi, after having calculated the file
offsets for each section.

Debugged-by: Kansu Dincer <kdincer@microsoft.com>
Debugged-by: Christian Nilsson <nikize@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
Michael Brown
2026-09-29 15:18:31 +01:00
parent 629e28b56c
commit 0e98032adb
3 changed files with 99 additions and 74 deletions
+27
View File
@@ -35,6 +35,8 @@ FILE_SECBOOT ( PERMITTED );
#include <ipxe/version.h>
#include <ipxe/sbat.h>
#include <ipxe/sbom.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/IndustryStandard/PeImage.h>
#include <config/general.h>
#include <config/branding.h>
@@ -130,3 +132,28 @@ const char sbat[ sizeof ( SBAT_DATA ) - 1 ] __sbat = SBAT_DATA;
/** SBOM data (without any NUL terminator) */
const char sbom[ sizeof ( SBOM_DATA ) - 1 ] __sbom = SBOM_DATA;
/** EFI debug directory */
const struct __attribute__ (( packed )) {
char magic[8];
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug;
EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds;
char name[ sizeof ( BUILD_NAME ) ];
} efi_debugdir __attribute__ (( section ( ".debugdir" ) )) = {
.magic = "EFIDEBUG",
.debug = {
.TimeDateStamp = 0x10d1a884,
.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW,
.SizeOfData = ( sizeof ( efi_debugdir ) -
sizeof ( efi_debugdir.debug ) ),
/* RVA and FileOffset will be updated by elf2efi */
.RVA = ( ( ( const void * ) &efi_debugdir.rsds ) -
( ( const void * ) &efi_debugdir ) ),
.FileOffset = ( ( ( const void * ) &efi_debugdir.rsds ) -
( ( const void * ) &efi_debugdir ) ),
},
.rsds = {
.Signature = CODEVIEW_SIGNATURE_RSDS,
},
.name = BUILD_NAME,
};
+2
View File
@@ -39,6 +39,8 @@ SECTIONS {
. = ALIGN ( _page_align );
.rodata : {
_rodata = .;
KEEP(*(.debugdir))
KEEP(*(.debugdir.*))
*(.srodata)
*(.srodata.*)
*(.rodata)
+70 -74
View File
@@ -33,7 +33,6 @@
#include <sys/mman.h>
#include <fcntl.h>
#include <elf.h>
#include <libgen.h>
#define EFI_HOSTONLY
#include <ipxe/efi/Uefi.h>
@@ -1109,83 +1108,17 @@ create_reloc_section ( struct pe_header *pe_header,
}
/**
* Create debug section
*
* @v pe_header PE file header
* @ret section Debug section
*/
static struct pe_section *
create_debug_section ( struct pe_header *pe_header, const char *filename ) {
struct pe_section *debug;
size_t section_memsz;
size_t section_filesz;
EFI_IMAGE_DATA_DIRECTORY *debugdir;
struct {
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY debug;
EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY rsds;
char name[32];
} *contents;
/* Allocate PE section */
section_filesz = section_memsz = sizeof ( *contents );
debug = xmalloc ( sizeof ( *debug ) + section_filesz );
memset ( debug, 0, sizeof ( *debug ) + section_filesz );
contents = ( void * ) debug->contents;
/* Place at end of headers */
pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( *contents );
pe_header->nt.OptionalHeader.SizeOfHeaders =
efi_file_align ( pe_header->nt.OptionalHeader.SizeOfHeaders );
pe_header->nt.OptionalHeader.SizeOfHeaders -= sizeof ( *contents );
/* Fill in section header details */
strncpy ( ( char * ) debug->hdr.Name, ".debug",
sizeof ( debug->hdr.Name ) );
debug->hdr.Misc.VirtualSize = section_memsz;
debug->hdr.VirtualAddress =
pe_header->nt.OptionalHeader.SizeOfHeaders;
debug->hdr.SizeOfRawData = section_filesz;
debug->hdr.PointerToRawData =
pe_header->nt.OptionalHeader.SizeOfHeaders;
debug->hidden = 1;
/* Create section contents */
contents->debug.TimeDateStamp = 0x10d1a884;
contents->debug.Type = EFI_IMAGE_DEBUG_TYPE_CODEVIEW;
contents->debug.SizeOfData =
( sizeof ( *contents ) - sizeof ( contents->debug ) );
contents->debug.RVA = ( debug->hdr.VirtualAddress +
offsetof ( typeof ( *contents ), rsds ) );
contents->debug.FileOffset = contents->debug.RVA;
contents->rsds.Signature = CODEVIEW_SIGNATURE_RSDS;
snprintf ( contents->name, sizeof ( contents->name ), "%s",
filename );
/* Update file header details */
pe_header->nt.OptionalHeader.SizeOfHeaders += sizeof ( *contents );
debugdir = &(pe_header->nt.OptionalHeader.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
debugdir->VirtualAddress = debug->hdr.VirtualAddress;
debugdir->Size = sizeof ( contents->debug );
return debug;
}
/**
* Write out PE file
* Assign file offsets
*
* @v pe_header PE file header
* @v pe_sections List of PE sections
* @v pe Output file
*/
static void write_pe_file ( struct pe_header *pe_header,
struct pe_section *pe_sections,
FILE *pe ) {
static void assign_offsets ( struct pe_header *pe_header,
struct pe_section *pe_sections ) {
struct pe_section *section;
unsigned long hdrmax;
unsigned long fpos;
unsigned long fposmax;
unsigned int count = 0;
/* Extend header length to reach first explicitly placed section */
hdrmax = -1UL;
@@ -1224,6 +1157,68 @@ static void write_pe_file ( struct pe_header *pe_header,
if ( fpos > fposmax )
fposmax = fpos;
}
}
/**
* Create debug directory
*
* @v pe_header PE file header
* @v pe_sections List of PE sections
*/
static void create_debug_directory ( struct pe_header *pe_header,
struct pe_section *pe_sections ) {
static const char magic[] = "EFIDEBUG";
EFI_IMAGE_DATA_DIRECTORY *debugdir;
struct {
char magic[8];
EFI_IMAGE_DEBUG_DIRECTORY_ENTRY dir;
} *debug;
struct pe_section *section;
/* Update partially constructed debug directory */
for ( section = pe_sections ; section ; section = section->next ) {
/* Find section starting with magic signature */
if ( section->hidden )
continue;
if ( section->hdr.SizeOfRawData < sizeof ( *debug ) )
continue;
debug = ( ( void * ) section->contents );
if ( memcmp ( debug->magic, magic,
sizeof ( debug->magic ) ) != 0 ) {
continue;
}
/* Update RVA and file offset */
debug->dir.RVA += section->hdr.VirtualAddress;
debug->dir.FileOffset += section->hdr.PointerToRawData;
/* Update file header */
debugdir = &(pe_header->nt.OptionalHeader.DataDirectory
[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG]);
debugdir->VirtualAddress =
( section->hdr.VirtualAddress +
offsetof ( typeof ( *debug ), dir ) );
debugdir->Size = sizeof ( debug->dir );
return;
}
eprintf ( "No debug directory found\n" );
exit ( 1 );
}
/**
* Write out PE file
*
* @v pe_header PE file header
* @v pe_sections List of PE sections
* @v pe Output file
*/
static void write_pe_file ( struct pe_header *pe_header,
struct pe_section *pe_sections,
FILE *pe ) {
struct pe_section *section;
unsigned int count = 0;
/* Write sections */
for ( section = pe_sections ; section ; section = section->next ) {
@@ -1353,10 +1348,11 @@ static void elf2pe ( const char *elf_name, const char *pe_name,
*(next_pe_section) = create_reloc_section ( &pe_header, pe_reltab );
next_pe_section = &(*next_pe_section)->next;
/* Create the .debug section */
*(next_pe_section) = create_debug_section ( &pe_header,
basename ( pe_name_tmp ) );
next_pe_section = &(*next_pe_section)->next;
/* Assign file offsets */
assign_offsets ( &pe_header, pe_sections );
/* Create the debug directory */
create_debug_directory ( &pe_header, pe_sections );
/* Write out PE file */
pe = fopen ( pe_name, "w" );