BaseTools: Add support for preserving build ID

Adds an optional flag that copies the GNU build-id note from the input
ELF file into the output PE/COFF firmware image as a dedicated ".bldid"
section. The build ID is emitted by the linker as a unique fingerprint
of the binary and allows custom post-build and debugging tools to
reliably match a firmware image against its corresponding unstripped
ELF and debug symbols, without relying on file names, timestamps, or
build paths.

This notable opts to use a non-standard section name ".bldid" to store
the build ID. This approach was chosen to keep genfw and the parsers simple
since the full "build-id" name would require redirecting the section name.
While this breaks from standard conventions, this is not impactful since
GenFW is already creating a non-standard artifact for the PE image with
the associated ELF symbol file.

Signed-off-by: Chris Fernald <chfernal@microsoft.com>
This commit is contained in:
Chris Fernald 2026-01-16 09:30:45 -08:00 committed by mergify[bot]
parent b551e8bb32
commit 27ac8fac0b
4 changed files with 110 additions and 8 deletions

View file

@ -130,6 +130,8 @@ STATIC UINT32 mHiiRsrcOffset;
STATIC UINT32 mRelocOffset;
STATIC UINT32 mDebugOffset;
STATIC UINT32 mExportOffset;
STATIC UINT32 mBuildIdOffset;
STATIC BOOLEAN mBuildIdFound;
//
// Used for RISC-V relocations.
//
@ -227,6 +229,10 @@ InitializeElf64 (
ElfFunctions->WriteExport = WriteExport64;
}
if (mBuildIdFlag) {
mCoffNbrSections++;
}
return TRUE;
}
@ -290,6 +296,22 @@ IsHiiRsrcShdr (
return (BOOLEAN) (strcmp((CHAR8*)mEhdr + Namedr->sh_offset + Shdr->sh_name, ELF_HII_SECTION_NAME) == 0);
}
STATIC
BOOLEAN
IsBuildIdShdr (
Elf_Shdr *Shdr
)
{
Elf_Shdr *Namedr = GetShdrByIndex(mEhdr->e_shstrndx);
if (Namedr->sh_offset + Shdr->sh_name >= mFileBufferSize) {
Error (NULL, 0, 3000, "Invalid", "IsBuildIdShdr: Name offset %lu is larger then file size %lu", mEhdr->e_shstrndx, mFileBufferSize);
exit(EXIT_FAILURE);
}
return (BOOLEAN) (strcmp((CHAR8*)mEhdr + Namedr->sh_offset + Shdr->sh_name, ELF_BUILD_ID_SECTION_NAME) == 0);
}
STATIC
BOOLEAN
IsSymbolShdr (
@ -1123,6 +1145,40 @@ ScanSections64 (
}
}
//
// The build-ID section.
//
mBuildIdOffset = mCoffOffset;
mBuildIdFound = FALSE;
if (mBuildIdFlag) {
for (i = 0; i < mEhdr->e_shnum; i++) {
Elf_Shdr *shdr = GetShdrByIndex(i);
if (IsBuildIdShdr(shdr)) {
if ((shdr->sh_addralign != 0) && (shdr->sh_addralign != 1)) {
// the alignment field is valid
if ((shdr->sh_addr & (shdr->sh_addralign - 1)) == 0) {
// if the section address is aligned we must align PE/COFF
mCoffOffset = (UINT32) ((mCoffOffset + shdr->sh_addralign - 1) & ~(shdr->sh_addralign - 1));
} else {
Error (NULL, 0, 3000, "Invalid", "Section address not aligned to its own alignment.");
}
}
if (shdr->sh_size != 0) {
mBuildIdOffset = mCoffOffset;
mCoffSectionsOffset[i] = mCoffOffset;
mCoffOffset += (UINT32) shdr->sh_size;
mCoffOffset = CoffAlign(mCoffOffset);
mBuildIdFound = TRUE;
}
break;
}
}
if (!mBuildIdFound) {
Warning (NULL, 0, 0, NULL, "Build ID section is not found in %s.", mInImageName);
}
}
mRelocOffset = mCoffOffset;
//
@ -1242,18 +1298,41 @@ ScanSections64 (
}
}
if ((mRelocOffset - mHiiRsrcOffset) > 0) {
CreateSectionHeader (".rsrc", mHiiRsrcOffset, mRelocOffset - mHiiRsrcOffset,
//
// Determine the end offset for .rsrc section based on whether build-ID is present
//
if (mBuildIdFound) {
Offset = mBuildIdOffset;
} else {
Offset = mRelocOffset;
}
if ((Offset - mHiiRsrcOffset) > 0) {
CreateSectionHeader (".rsrc", mHiiRsrcOffset, Offset - mHiiRsrcOffset,
EFI_IMAGE_SCN_CNT_INITIALIZED_DATA
| EFI_IMAGE_SCN_MEM_READ);
NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = mRelocOffset - mHiiRsrcOffset;
NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = Offset - mHiiRsrcOffset;
NtHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = mHiiRsrcOffset;
} else {
// Don't make a section of size 0.
NtHdr->Pe32Plus.FileHeader.NumberOfSections--;
}
//
// Add build-ID section if requested and found
//
if (mBuildIdFlag) {
if (mBuildIdFound) {
CreateSectionHeader (".bldid", mBuildIdOffset, mRelocOffset - mBuildIdOffset,
EFI_IMAGE_SCN_CNT_INITIALIZED_DATA
| EFI_IMAGE_SCN_MEM_READ);
} else {
// Don't make a section of size 0, decrement the section count
NtHdr->Pe32Plus.FileHeader.NumberOfSections--;
}
}
}
STATIC
@ -1281,6 +1360,9 @@ WriteSections64 (
case SECTION_DATA:
Filter = IsDataShdr;
break;
case SECTION_BUILD_ID:
Filter = IsBuildIdShdr;
break;
default:
return FALSE;
}

View file

@ -210,6 +210,11 @@ ConvertElf (
if (!ElfFunctions.WriteSections (SECTION_HII)) {
return FALSE;
}
if (mBuildIdFlag) {
if (!ElfFunctions.WriteSections (SECTION_BUILD_ID)) {
return FALSE;
}
}
//
// Translate and write relocations.

View file

@ -24,14 +24,16 @@ extern UINT32 mTableOffset;
extern UINT32 mOutImageType;
extern UINT32 mFileBufferSize;
extern BOOLEAN mExportFlag;
extern BOOLEAN mBuildIdFlag;
//
// Common EFI specific data.
//
#define ELF_HII_SECTION_NAME ".hii"
#define ELF_STRTAB_SECTION_NAME ".strtab"
#define MAX_COFF_ALIGNMENT 0x10000
#define ELF_SYMBOL_SECTION_NAME ".symtab"
#define ELF_HII_SECTION_NAME ".hii"
#define ELF_STRTAB_SECTION_NAME ".strtab"
#define MAX_COFF_ALIGNMENT 0x10000
#define ELF_SYMBOL_SECTION_NAME ".symtab"
#define ELF_BUILD_ID_SECTION_NAME ".build-id"
//
// Platform Runtime Mechanism (PRM) specific data.
@ -77,7 +79,8 @@ typedef enum {
SECTION_TEXT,
SECTION_HII,
SECTION_DATA,
SECTION_SYMBOL
SECTION_SYMBOL,
SECTION_BUILD_ID
} SECTION_FILTER_TYPES;
//

View file

@ -89,6 +89,7 @@ UINT32 mOutImageType = FW_DUMMY_IMAGE;
BOOLEAN mIsConvertXip = FALSE;
BOOLEAN mExportFlag = FALSE;
BOOLEAN mNoNxCompat = FALSE;
BOOLEAN mBuildIdFlag = FALSE;
STATIC
EFI_STATUS
@ -290,6 +291,10 @@ Returns:
fprintf (stdout, " --nonxcompat Do not set the IMAGE_DLLCHARACTERISTICS_NX_COMPAT bit \n\
of the optional header in the PE header even if the \n\
requirements are met.\n");
fprintf (stdout, " --build-id Preserve the .build-id section from the ELF image\n\
and copy it to a .bldid section in the PE image.\n\
This option can be used together with -e or -t.\n\
It doesn't work for other options.\n");
fprintf (stdout, " -v, --verbose Turn on verbose output with informational messages.\n");
fprintf (stdout, " -q, --quiet Disable all messages except key message and fatal error\n");
fprintf (stdout, " -d, --debug level Enable debug messages, at input debug level.\n");
@ -1576,6 +1581,13 @@ Returns:
continue;
}
if (stricmp (argv[0], "--build-id") == 0) {
mBuildIdFlag = TRUE;
argc--;
argv++;
continue;
}
if (argv[0][0] == '-') {
Error (NULL, 0, 1000, "Unknown option", argv[0]);
goto Finish;