[llvm-branch-commits] MC: Add ELF section and directive for specifying a section's preferred alignment. (PR #150151)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Tue Jul 22 18:30:52 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mc
Author: Peter Collingbourne (pcc)
<details>
<summary>Changes</summary>
The new ``SHT_LLVM_MIN_ADDRALIGN`` section is used to specify the
minimum alignment of a section where that differs from its preferred
alignment. Its ``sh_link`` field identifies the section whose alignment is
being specified, its ``sh_addralign`` field specifies the linked section's
minimum alignment and the ``sh_addralign`` field of the linked section's
section header specifies its preferred alignment. This section has the
``SHF_EXCLUDE`` flag so that it is stripped from the final executable or
shared library, and the ``SHF_LINK_ORDER`` flag so that the ``sh_link``
field is updated by tools such as ``ld -r`` and ``objcopy``. The contents
of the section must be empty.
The new asm directive:
.prefalign n
specifies that the preferred alignment of the current section is
determined by taking the maximum of ``n`` and the section's minimum
alignment, and causes an ``SHT_LLVM_MIN_ADDRALIGN`` section to be emitted
if necessary.
---
Full diff: https://github.com/llvm/llvm-project/pull/150151.diff
13 Files Affected:
- (modified) llvm/docs/Extensions.rst (+22)
- (modified) llvm/include/llvm/BinaryFormat/ELF.h (+1)
- (modified) llvm/include/llvm/MC/MCObjectStreamer.h (+1)
- (modified) llvm/include/llvm/MC/MCSection.h (+12)
- (modified) llvm/include/llvm/MC/MCStreamer.h (+2)
- (modified) llvm/lib/MC/ELFObjectWriter.cpp (+13-4)
- (modified) llvm/lib/MC/MCAsmStreamer.cpp (+6)
- (modified) llvm/lib/MC/MCObjectStreamer.cpp (+4)
- (modified) llvm/lib/MC/MCParser/AsmParser.cpp (+20)
- (modified) llvm/lib/MC/MCStreamer.cpp (+1)
- (modified) llvm/lib/Object/ELF.cpp (+1)
- (added) llvm/test/MC/ELF/prefalign-errors.s (+5)
- (added) llvm/test/MC/ELF/prefalign.s (+47)
``````````diff
diff --git a/llvm/docs/Extensions.rst b/llvm/docs/Extensions.rst
index d8fb87b6998ad..e6b2db0745623 100644
--- a/llvm/docs/Extensions.rst
+++ b/llvm/docs/Extensions.rst
@@ -601,6 +601,28 @@ sees fit (generally the section that would provide the best locality).
.. _CFI jump table: https://clang.llvm.org/docs/ControlFlowIntegrityDesign.html#forward-edge-cfi-for-indirect-function-calls
+``SHT_LLVM_MIN_ADDRALIGN`` Section (minimum section alignment)
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+This section is used to specify the minimum alignment of a section
+where that differs from its preferred alignment. Its ``sh_link``
+field identifies the section whose alignment is being specified, its
+``sh_addralign`` field specifies the linked section's minimum alignment
+and the ``sh_addralign`` field of the linked section's section header
+specifies its preferred alignment. This section has the ``SHF_EXCLUDE``
+flag so that it is stripped from the final executable or shared library,
+and the ``SHF_LINK_ORDER`` flag so that the ``sh_link`` field is updated
+by tools such as ``ld -r`` and ``objcopy``. The contents of the section
+must be empty.
+
+.. code-block:: gas
+
+ .prefalign n
+
+Specifies that the preferred alignment of the current section is
+determined by taking the maximum of ``n`` and the section's minimum
+alignment, and causes an ``SHT_LLVM_MIN_ADDRALIGN`` section to be emitted
+if necessary.
+
CodeView-Dependent
------------------
diff --git a/llvm/include/llvm/BinaryFormat/ELF.h b/llvm/include/llvm/BinaryFormat/ELF.h
index e4f82ad96a084..95600f39153d4 100644
--- a/llvm/include/llvm/BinaryFormat/ELF.h
+++ b/llvm/include/llvm/BinaryFormat/ELF.h
@@ -1160,6 +1160,7 @@ enum : unsigned {
SHT_LLVM_LTO = 0x6fff4c0c, // .llvm.lto for fat LTO.
SHT_LLVM_JT_SIZES = 0x6fff4c0d, // LLVM jump tables sizes.
SHT_LLVM_CFI_JUMP_TABLE = 0x6fff4c0e, // LLVM CFI jump table.
+ SHT_LLVM_MIN_ADDRALIGN = 0x6fff4c0f, // Minimum alignment specification.
// Android's experimental support for SHT_RELR sections.
// https://android.googlesource.com/platform/bionic/+/b7feec74547f84559a1467aca02708ff61346d2a/libc/include/elf.h#512
SHT_ANDROID_RELR = 0x6fffff00, // Relocation entries; only offsets.
diff --git a/llvm/include/llvm/MC/MCObjectStreamer.h b/llvm/include/llvm/MC/MCObjectStreamer.h
index 319e131999d48..27de57f2ccd91 100644
--- a/llvm/include/llvm/MC/MCObjectStreamer.h
+++ b/llvm/include/llvm/MC/MCObjectStreamer.h
@@ -113,6 +113,7 @@ class MCObjectStreamer : public MCStreamer {
unsigned MaxBytesToEmit = 0) override;
void emitCodeAlignment(Align ByteAlignment, const MCSubtargetInfo *STI,
unsigned MaxBytesToEmit = 0) override;
+ void emitPrefAlign(Align Alignment) override;
void emitValueToOffset(const MCExpr *Offset, unsigned char Value,
SMLoc Loc) override;
void emitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column,
diff --git a/llvm/include/llvm/MC/MCSection.h b/llvm/include/llvm/MC/MCSection.h
index 313071ec75033..61ae6dff4d146 100644
--- a/llvm/include/llvm/MC/MCSection.h
+++ b/llvm/include/llvm/MC/MCSection.h
@@ -82,6 +82,7 @@ class LLVM_ABI MCSection {
MCSymbol *End = nullptr;
/// The alignment requirement of this section.
Align Alignment;
+ MaybeAlign PreferredAlignment;
/// The section index in the assemblers section list.
unsigned Ordinal = 0;
@@ -147,6 +148,17 @@ class LLVM_ABI MCSection {
Alignment = MinAlignment;
}
+ Align getPreferredAlignment() const {
+ if (!PreferredAlignment || Alignment > *PreferredAlignment)
+ return Alignment;
+ return *PreferredAlignment;
+ }
+
+ void ensurePreferredAlignment(Align PrefAlign) {
+ if (!PreferredAlignment || PrefAlign > *PreferredAlignment)
+ PreferredAlignment = PrefAlign;
+ }
+
unsigned getOrdinal() const { return Ordinal; }
void setOrdinal(unsigned Value) { Ordinal = Value; }
diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h
index 4b91dbc794682..e7947657e1356 100644
--- a/llvm/include/llvm/MC/MCStreamer.h
+++ b/llvm/include/llvm/MC/MCStreamer.h
@@ -832,6 +832,8 @@ class LLVM_ABI MCStreamer {
virtual void emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI,
unsigned MaxBytesToEmit = 0);
+ virtual void emitPrefAlign(Align A);
+
/// Emit some number of copies of \p Value until the byte offset \p
/// Offset is reached.
///
diff --git a/llvm/lib/MC/ELFObjectWriter.cpp b/llvm/lib/MC/ELFObjectWriter.cpp
index 9f52b3e3e85c0..7709cf56ffd32 100644
--- a/llvm/lib/MC/ELFObjectWriter.cpp
+++ b/llvm/lib/MC/ELFObjectWriter.cpp
@@ -926,10 +926,10 @@ void ELFWriter::writeSectionHeader(uint32_t GroupSymbolIndex, uint64_t Offset,
sh_link = Sym->getSection().getOrdinal();
}
- writeSectionHeaderEntry(StrTabBuilder.getOffset(Section.getName()),
- Section.getType(), Section.getFlags(), 0, Offset,
- Size, sh_link, sh_info, Section.getAlign(),
- Section.getEntrySize());
+ writeSectionHeaderEntry(
+ StrTabBuilder.getOffset(Section.getName()), Section.getType(),
+ Section.getFlags(), 0, Offset, Size, sh_link, sh_info,
+ Section.getPreferredAlignment(), Section.getEntrySize());
}
void ELFWriter::writeSectionHeaders() {
@@ -1062,6 +1062,15 @@ uint64_t ELFWriter::writeObject() {
Relocations.push_back(RelSection);
}
+ if (Sec.getPreferredAlignment() != Sec.getAlign()) {
+ MCSectionELF *MinAlign = Ctx.getELFSection(
+ ".llvm.minalign", ELF::SHT_LLVM_MIN_ADDRALIGN,
+ ELF::SHF_EXCLUDE | ELF::SHF_LINK_ORDER, 0, "", false, 0,
+ cast<MCSymbolELF>(Section.getBeginSymbol()));
+ MinAlign->setOrdinal(addToSectionTable(MinAlign));
+ MinAlign->setAlignment(Sec.getAlign());
+ }
+
if (GroupIdxEntry) {
auto &Members = Groups[GroupMap[*GroupIdxEntry]];
Members.second.push_back(Section.getOrdinal());
diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp
index 67c53e01a6111..4d53607400a27 100644
--- a/llvm/lib/MC/MCAsmStreamer.cpp
+++ b/llvm/lib/MC/MCAsmStreamer.cpp
@@ -275,6 +275,7 @@ class MCAsmStreamer final : public MCStreamer {
void emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI,
unsigned MaxBytesToEmit = 0) override;
+ void emitPrefAlign(Align Alignment) override;
void emitValueToOffset(const MCExpr *Offset,
unsigned char Value,
@@ -1540,6 +1541,11 @@ void MCAsmStreamer::emitCodeAlignment(Align Alignment,
emitAlignmentDirective(Alignment.value(), std::nullopt, 1, MaxBytesToEmit);
}
+void MCAsmStreamer::emitPrefAlign(Align Alignment) {
+ OS << ".prefalign " << Alignment.value();
+ EmitEOL();
+}
+
void MCAsmStreamer::emitValueToOffset(const MCExpr *Offset,
unsigned char Value,
SMLoc Loc) {
diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp
index d5b8f22463894..e860f6c61c5f7 100644
--- a/llvm/lib/MC/MCObjectStreamer.cpp
+++ b/llvm/lib/MC/MCObjectStreamer.cpp
@@ -567,6 +567,10 @@ void MCObjectStreamer::emitCodeAlignment(Align Alignment,
}
}
+void MCObjectStreamer::emitPrefAlign(Align Alignment) {
+ getCurrentSectionOnly()->ensurePreferredAlignment(Alignment);
+}
+
void MCObjectStreamer::emitValueToOffset(const MCExpr *Offset,
unsigned char Value,
SMLoc Loc) {
diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp
index 77bf84364c5a3..2e85c08852c15 100644
--- a/llvm/lib/MC/MCParser/AsmParser.cpp
+++ b/llvm/lib/MC/MCParser/AsmParser.cpp
@@ -418,6 +418,7 @@ class AsmParser : public MCAsmParser {
DK_P2ALIGN,
DK_P2ALIGNW,
DK_P2ALIGNL,
+ DK_PREFALIGN,
DK_ORG,
DK_FILL,
DK_ENDR,
@@ -565,6 +566,7 @@ class AsmParser : public MCAsmParser {
bool parseDirectiveOrg(); // ".org"
// ".align{,32}", ".p2align{,w,l}"
bool parseDirectiveAlign(bool IsPow2, uint8_t ValueSize);
+ bool parseDirectivePrefAlign();
// ".file", ".line", ".loc", ".loc_label", ".stabs"
bool parseDirectiveFile(SMLoc DirectiveLoc);
@@ -2000,6 +2002,8 @@ bool AsmParser::parseStatement(ParseStatementInfo &Info,
return parseDirectiveAlign(/*IsPow2=*/true, /*ExprSize=*/2);
case DK_P2ALIGNL:
return parseDirectiveAlign(/*IsPow2=*/true, /*ExprSize=*/4);
+ case DK_PREFALIGN:
+ return parseDirectivePrefAlign();
case DK_ORG:
return parseDirectiveOrg();
case DK_FILL:
@@ -3426,6 +3430,21 @@ bool AsmParser::parseDirectiveAlign(bool IsPow2, uint8_t ValueSize) {
return ReturnVal;
}
+bool AsmParser::parseDirectivePrefAlign() {
+ SMLoc AlignmentLoc = getLexer().getLoc();
+ int64_t Alignment;
+ if (checkForValidSection() || parseAbsoluteExpression(Alignment))
+ return true;
+ if (parseEOL())
+ return true;
+
+ if (!isPowerOf2_64(Alignment))
+ return Error(AlignmentLoc, "alignment must be a power of 2");
+ getStreamer().emitPrefAlign(Align(Alignment));
+
+ return false;
+}
+
/// parseDirectiveFile
/// ::= .file filename
/// ::= .file number [directory] filename [md5 checksum] [source source-text]
@@ -5377,6 +5396,7 @@ void AsmParser::initializeDirectiveKindMap() {
DirectiveKindMap[".p2align"] = DK_P2ALIGN;
DirectiveKindMap[".p2alignw"] = DK_P2ALIGNW;
DirectiveKindMap[".p2alignl"] = DK_P2ALIGNL;
+ DirectiveKindMap[".prefalign"] = DK_PREFALIGN;
DirectiveKindMap[".org"] = DK_ORG;
DirectiveKindMap[".fill"] = DK_FILL;
DirectiveKindMap[".zero"] = DK_ZERO;
diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp
index c3ecf8fc717f5..eeca414da580d 100644
--- a/llvm/lib/MC/MCStreamer.cpp
+++ b/llvm/lib/MC/MCStreamer.cpp
@@ -1329,6 +1329,7 @@ void MCStreamer::emitFill(const MCExpr &NumBytes, uint64_t Value, SMLoc Loc) {}
void MCStreamer::emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr,
SMLoc Loc) {}
void MCStreamer::emitValueToAlignment(Align, int64_t, uint8_t, unsigned) {}
+void MCStreamer::emitPrefAlign(Align A) {}
void MCStreamer::emitCodeAlignment(Align Alignment, const MCSubtargetInfo *STI,
unsigned MaxBytesToEmit) {}
void MCStreamer::emitValueToOffset(const MCExpr *Offset, unsigned char Value,
diff --git a/llvm/lib/Object/ELF.cpp b/llvm/lib/Object/ELF.cpp
index 788c6020a7f99..ab23a0b1f25b9 100644
--- a/llvm/lib/Object/ELF.cpp
+++ b/llvm/lib/Object/ELF.cpp
@@ -322,6 +322,7 @@ StringRef llvm::object::getELFSectionTypeName(uint32_t Machine, unsigned Type) {
STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_LTO);
STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_JT_SIZES)
STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_CFI_JUMP_TABLE)
+ STRINGIFY_ENUM_CASE(ELF, SHT_LLVM_MIN_ADDRALIGN)
STRINGIFY_ENUM_CASE(ELF, SHT_GNU_SFRAME);
STRINGIFY_ENUM_CASE(ELF, SHT_GNU_ATTRIBUTES);
STRINGIFY_ENUM_CASE(ELF, SHT_GNU_HASH);
diff --git a/llvm/test/MC/ELF/prefalign-errors.s b/llvm/test/MC/ELF/prefalign-errors.s
new file mode 100644
index 0000000000000..363638f9bcb1e
--- /dev/null
+++ b/llvm/test/MC/ELF/prefalign-errors.s
@@ -0,0 +1,5 @@
+// RUN: not llvm-mc -filetype=asm -triple x86_64-pc-linux-gnu %s -o - 2>&1 | FileCheck %s
+
+.section .text.f1,"ax", at progbits
+// CHECK: error: alignment must be a power of 2
+.prefalign 3
diff --git a/llvm/test/MC/ELF/prefalign.s b/llvm/test/MC/ELF/prefalign.s
new file mode 100644
index 0000000000000..f3537029b23c1
--- /dev/null
+++ b/llvm/test/MC/ELF/prefalign.s
@@ -0,0 +1,47 @@
+// RUN: llvm-mc -filetype=asm -triple x86_64-pc-linux-gnu %s -o - | FileCheck --check-prefix=ASM %s
+// RUN: llvm-mc -filetype=obj -triple x86_64-pc-linux-gnu %s -o - | llvm-readelf -SW - | FileCheck --check-prefix=OBJ %s
+
+// Minimum alignment = preferred alignment, no SHT_LLVM_MIN_ADDRALIGN needed.
+// ASM: .section .text.f1
+// ASM: .p2align 2
+// ASM: .prefalign 4
+// OBJ: .text.f1
+// OBJ-NOT: .llvm.minalign
+.section .text.f1,"ax", at progbits
+.p2align 2
+.prefalign 4
+
+// Minimum alignment < preferred alignment, SHT_LLVM_MIN_ADDRALIGN emitted.
+// ASM: .section .text.f2
+// ASM: .p2align 2
+// ASM: .prefalign 8
+// OBJ: [ 4] .text.f2 PROGBITS 0000000000000000 000040 000000 00 AX 0 0 8
+// OBJ: [ 5] .llvm.minalign LLVM_MIN_ADDRALIGN 0000000000000000 000000 000000 00 LE 4 0 4
+.section .text.f2,"ax", at progbits
+.p2align 2
+.prefalign 8
+
+// Minimum alignment > preferred alignment, preferred alignment rounded up to
+// minimum alignment. No SHT_LLVM_MIN_ADDRALIGN emitted.
+// ASM: .section .text.f3
+// ASM: .p2align 3
+// ASM: .prefalign 4
+// OBJ: .text.f3
+// OBJ-NOT: .llvm.minalign
+.section .text.f3,"ax", at progbits
+.p2align 3
+.prefalign 4
+
+// Maximum of all .prefalign directives written to object file.
+// ASM: .section .text.f4
+// ASM: .p2align 2
+// ASM: .prefalign 8
+// ASM: .prefalign 16
+// ASM: .prefalign 8
+// OBJ: [ 7] .text.f4 PROGBITS 0000000000000000 000040 000000 00 AX 0 0 16
+// OBJ: [ 8] .llvm.minalign LLVM_MIN_ADDRALIGN 0000000000000000 000000 000000 00 LE 7 0 4
+.section .text.f4,"ax", at progbits
+.p2align 2
+.prefalign 8
+.prefalign 16
+.prefalign 8
``````````
</details>
https://github.com/llvm/llvm-project/pull/150151
More information about the llvm-branch-commits
mailing list