2026-07-10
The asker is building an EDK2/UEFI binary (almost certainly a shim-style bootloader) and needs to embed an SBAT (Secure Boot Advanced Targeting) revocation metadata blob into a dedicated PE section named .sbat. Shim's verifier reads this section by name at boot time, so the section must exist in the final image with that exact name — not just the data, the named container.
Their code declares the variable via #pragma section(".sbat", read) and __declspec(allocate(".sbat")), then uses /INCLUDE:sbat to keep the symbol alive against dead-strip. The symbol survives, but the .sbat section vanishes from the output PE.
Why this is subtle: the MSVC linker aggressively merges sections whose names share a common prefix and compatible attributes. Any section beginning with .rdata gets folded into .rdata; anything read-only-data-like with a short name may be merged into an existing section. The read attribute in #pragma section maps to characteristics (IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_INITIALIZED_DATA) that look identical to .rdata, and link.exe will happily coalesce them. The data survives; the name does not.
Direction toward a fix:
/MERGE:.sbat=.sbat — a no-op merge that pins the name. Some builds instead use /SECTION:.sbat,ER or ,RD to give it attributes that don't match .rdata.dumpbin /headers or llvm-readobj --sections after each build — don't trust that the symbol's presence implies the section's presence./OPT:REF or /OPT:ICF is folding it. /INCLUDE prevents REF elimination of the symbol, but ICF and section merging are separate passes.tools_def.txt or the DSC — a project-level /MERGE may be overriding your intent. Inspect the actual linker command line, not just your pragmas.Gotchas: shim on Linux uses objcopy --set-section-flags to place SBAT with specific flags (alloc,readonly,data,contents). On MSVC/PE, the analog is section characteristics, and the shim verifier is strict about them. Even if the name is preserved, wrong characteristics can cause boot-time rejection. Also: volatile on the array doesn't prevent section merging — it only prevents compiler-level optimization of accesses.
/INCLUDE.
