[lld] 66f8ac8 - [ELF] Support (TYPE=<value>) to customize the output section type
Fangrui Song via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 17 12:11:02 PST 2022
Author: Fangrui Song
Date: 2022-02-17T12:10:58-08:00
New Revision: 66f8ac8d3604d67599734c3fd272032e9448aca2
URL: https://github.com/llvm/llvm-project/commit/66f8ac8d3604d67599734c3fd272032e9448aca2
DIFF: https://github.com/llvm/llvm-project/commit/66f8ac8d3604d67599734c3fd272032e9448aca2.diff
LOG: [ELF] Support (TYPE=<value>) to customize the output section type
The current output section type allows to set the ELF section type to
SHT_PROGBITS or SHT_NOLOAD. This patch allows an arbitrary section value
to be specified. Some common SHT_* literal names are supported as well.
```
SECTIONS {
note (TYPE=SHT_NOTE) : { BYTE(8) *(note) }
init_array ( TYPE=14 ) : { QUAD(14) }
fini_array (TYPE = SHT_FINI_ARRAY) : { QUAD(15) }
}
```
When `sh_type` is specified, it is an error if an input section has a different type.
Our syntax is compatible with GNU ld 2.39 (https://sourceware.org/bugzilla/show_bug.cgi?id=28841).
Reviewed By: peter.smith
Differential Revision: https://reviews.llvm.org/D118840
Added:
lld/test/ELF/linkerscript/custom-section-type.s
Modified:
lld/ELF/OutputSections.cpp
lld/ELF/OutputSections.h
lld/ELF/ScriptParser.cpp
lld/docs/ELF/linker_script.rst
lld/test/ELF/linkerscript/noload.s
Removed:
################################################################################
diff --git a/lld/ELF/OutputSections.cpp b/lld/ELF/OutputSections.cpp
index 85c343b148c3a..2b5deecdcec75 100644
--- a/lld/ELF/OutputSections.cpp
+++ b/lld/ELF/OutputSections.cpp
@@ -108,32 +108,34 @@ void OutputSection::recordSection(InputSectionBase *isec) {
// isec. Also check whether the InputSection flags and type are consistent with
// other InputSections.
void OutputSection::commitSection(InputSection *isec) {
+ if (LLVM_UNLIKELY(type != isec->type)) {
+ if (hasInputSections || typeIsSet) {
+ if (typeIsSet || !canMergeToProgbits(type) ||
+ !canMergeToProgbits(isec->type)) {
+ errorOrWarn("section type mismatch for " + isec->name + "\n>>> " +
+ toString(isec) + ": " +
+ getELFSectionTypeName(config->emachine, isec->type) +
+ "\n>>> output section " + name + ": " +
+ getELFSectionTypeName(config->emachine, type));
+ }
+ type = SHT_PROGBITS;
+ } else {
+ type = isec->type;
+ }
+ }
if (!hasInputSections) {
// If IS is the first section to be added to this section,
// initialize type, entsize and flags from isec.
hasInputSections = true;
- type = isec->type;
entsize = isec->entsize;
flags = isec->flags;
} else {
// Otherwise, check if new type or flags are compatible with existing ones.
if ((flags ^ isec->flags) & SHF_TLS)
- error("incompatible section flags for " + name + "\n>>> " + toString(isec) +
- ": 0x" + utohexstr(isec->flags) + "\n>>> output section " + name +
- ": 0x" + utohexstr(flags));
-
- if (type != isec->type) {
- if (!canMergeToProgbits(type) || !canMergeToProgbits(isec->type))
- error("section type mismatch for " + isec->name + "\n>>> " +
- toString(isec) + ": " +
- getELFSectionTypeName(config->emachine, isec->type) +
- "\n>>> output section " + name + ": " +
- getELFSectionTypeName(config->emachine, type));
- type = SHT_PROGBITS;
- }
+ error("incompatible section flags for " + name + "\n>>> " +
+ toString(isec) + ": 0x" + utohexstr(isec->flags) +
+ "\n>>> output section " + name + ": 0x" + utohexstr(flags));
}
- if (noload)
- type = SHT_NOBITS;
isec->parent = this;
uint64_t andMask =
@@ -448,14 +450,14 @@ template <class ELFT> void OutputSection::writeTo(uint8_t *buf) {
writeInt(buf + data->offset, data->expression().getValue(), data->size);
}
-static void finalizeShtGroup(OutputSection *os,
- InputSection *section) {
- assert(config->relocatable);
-
+static void finalizeShtGroup(OutputSection *os, InputSection *section) {
// sh_link field for SHT_GROUP sections should contain the section index of
// the symbol table.
os->link = in.symTab->getParent()->sectionIndex;
+ if (!section)
+ return;
+
// sh_info then contain index of an entry in symbol table section which
// provides signature of the section group.
ArrayRef<Symbol *> symbols = section->file->getSymbols();
diff --git a/lld/ELF/OutputSections.h b/lld/ELF/OutputSections.h
index 60a3629e2f256..005537151e530 100644
--- a/lld/ELF/OutputSections.h
+++ b/lld/ELF/OutputSections.h
@@ -92,7 +92,7 @@ class OutputSection final : public SectionCommand, public SectionBase {
std::string memoryRegionName;
std::string lmaRegionName;
bool nonAlloc = false;
- bool noload = false;
+ bool typeIsSet = false;
bool expressionsUseSymbols = false;
bool usedInExpression = false;
bool inOverlay = false;
diff --git a/lld/ELF/ScriptParser.cpp b/lld/ELF/ScriptParser.cpp
index 9d9eb123eecf2..595050a83a386 100644
--- a/lld/ELF/ScriptParser.cpp
+++ b/lld/ELF/ScriptParser.cpp
@@ -786,19 +786,45 @@ Expr ScriptParser::readAssert() {
};
}
+#define ECase(X) \
+ { #X, X }
+constexpr std::pair<const char *, unsigned> typeMap[] = {
+ ECase(SHT_PROGBITS), ECase(SHT_NOTE), ECase(SHT_NOBITS),
+ ECase(SHT_INIT_ARRAY), ECase(SHT_FINI_ARRAY), ECase(SHT_PREINIT_ARRAY),
+};
+#undef ECase
+
// Tries to read the special directive for an output section definition which
-// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)" or "(OVERLAY)".
-// Tok1 and Tok2 are next 2 tokens peeked. See comment for readSectionAddressType below.
+// can be one of following: "(NOLOAD)", "(COPY)", "(INFO)", "(OVERLAY)", and
+// "(TYPE=<value>)".
+// Tok1 and Tok2 are next 2 tokens peeked. See comment for
+// readSectionAddressType below.
bool ScriptParser::readSectionDirective(OutputSection *cmd, StringRef tok1, StringRef tok2) {
if (tok1 != "(")
return false;
- if (tok2 != "NOLOAD" && tok2 != "COPY" && tok2 != "INFO" && tok2 != "OVERLAY")
+ if (tok2 != "NOLOAD" && tok2 != "COPY" && tok2 != "INFO" &&
+ tok2 != "OVERLAY" && tok2 != "TYPE")
return false;
expect("(");
if (consume("NOLOAD")) {
- cmd->noload = true;
cmd->type = SHT_NOBITS;
+ cmd->typeIsSet = true;
+ } else if (consume("TYPE")) {
+ expect("=");
+ StringRef value = peek();
+ auto it = llvm::find_if(typeMap, [=](auto e) { return e.first == value; });
+ if (it != std::end(typeMap)) {
+ // The value is a recognized literal SHT_*.
+ cmd->type = it->second;
+ skip();
+ } else if (value.startswith("SHT_")) {
+ setError("unknown section type " + value);
+ } else {
+ // Otherwise, read an expression.
+ cmd->type = readExpr()().getValue();
+ }
+ cmd->typeIsSet = true;
} else {
skip(); // This is "COPY", "INFO" or "OVERLAY".
cmd->nonAlloc = true;
@@ -819,7 +845,11 @@ bool ScriptParser::readSectionDirective(OutputSection *cmd, StringRef tok1, Stri
// https://sourceware.org/binutils/docs/ld/Output-Section-Address.html
// https://sourceware.org/binutils/docs/ld/Output-Section-Type.html
void ScriptParser::readSectionAddressType(OutputSection *cmd) {
- if (readSectionDirective(cmd, peek(), peek2()))
+ // Temporarily set inExpr to support TYPE=<value> without spaces.
+ bool saved = std::exchange(inExpr, true);
+ bool isDirective = readSectionDirective(cmd, peek(), peek2());
+ inExpr = saved;
+ if (isDirective)
return;
cmd->addrExpr = readExpr();
diff --git a/lld/docs/ELF/linker_script.rst b/lld/docs/ELF/linker_script.rst
index b94017b17158d..085b3aa77186e 100644
--- a/lld/docs/ELF/linker_script.rst
+++ b/lld/docs/ELF/linker_script.rst
@@ -102,6 +102,12 @@ When an *OutputSection* *S* has ``(type)``, LLD will set ``sh_type`` or
- ``NOLOAD``: set ``sh_type`` to ``SHT_NOBITS``.
- ``COPY``, ``INFO``, ``OVERLAY``: clear the ``SHF_ALLOC`` bit in ``sh_flags``.
+- ``TYPE=<value>``: set ``sh_type`` to the specified value. ``<value>`` must be
+ an integer or one of ``SHT_PROGBITS, SHT_NOTE, SHT_NOBITS, SHT_INIT_ARRAY,
+ SHT_FINI_ARRAY, SHT_PREINIT_ARRAY``.
+
+When ``sh_type`` is specified, it is an error if an input section in *S* has a
+
diff erent type.
Output section alignment
------------------------
diff --git a/lld/test/ELF/linkerscript/custom-section-type.s b/lld/test/ELF/linkerscript/custom-section-type.s
new file mode 100644
index 0000000000000..21ef9922e25ec
--- /dev/null
+++ b/lld/test/ELF/linkerscript/custom-section-type.s
@@ -0,0 +1,89 @@
+# REQUIRES: x86
+## TYPE=<value> customizes the output section type.
+
+# RUN: rm -rf %t && split-file %s %t
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t/a.s -o %t/a.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %t/mismatch.s -o %t/mismatch.o
+# RUN: ld.lld -T %t/a.lds %t/a.o -o %t/a
+# RUN: llvm-readelf -S %t/a | FileCheck %s
+
+# RUN: ld.lld -r -T %t/a.lds %t/a.o -o %t/a.ro
+# RUN: llvm-readelf -S %t/a.ro | FileCheck %s
+
+# CHECK: [Nr] Name Type Address Off Size ES Flg Lk Inf Al
+# CHECK-NEXT: [ 0] NULL [[#%x,]] [[#%x,]] 000000 00 0 0 0
+# CHECK-NEXT: [ 1] progbits PROGBITS [[#%x,]] [[#%x,]] 000001 00 A 0 0 1
+# CHECK-NEXT: [ 2] note NOTE [[#%x,]] [[#%x,]] 000002 00 A 0 0 1
+# CHECK-NEXT: [ 3] nobits NOBITS [[#%x,]] [[#%x,]] 000001 00 A 0 0 1
+# CHECK-NEXT: [ 4] init_array INIT_ARRAY [[#%x,]] [[#%x,]] 000008 00 A 0 0 1
+# CHECK-NEXT: [ 5] fini_array FINI_ARRAY [[#%x,]] [[#%x,]] 000008 00 A 0 0 1
+# CHECK-NEXT: [ 6] preinit_array PREINIT_ARRAY [[#%x,]] [[#%x,]] 000008 00 A 0 0 1
+# CHECK-NEXT: [ 7] group GROUP [[#%x,]] [[#%x,]] 000004 00 A [[#SYMTAB:]] 0 1
+# CHECK-NEXT: [ 8] expr 0x42: <unknown> [[#%x,]] [[#%x,]] 000001 00 A 0 0 1
+# CHECK: [[[#SYMTAB]]] .symtab SYMTAB
+
+# RUN: not ld.lld -T %t/a.lds %t/a.o %t/mismatch.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR1
+
+# ERR1: error: section type mismatch for progbits
+# ERR1-NEXT: >>> {{.*}}.o:(progbits): SHT_NOTE
+# ERR1-NEXT: >>> output section progbits: SHT_PROGBITS
+# ERR1: error: section type mismatch for expr
+# ERR1-NEXT: >>> {{.*}}.o:(expr): Unknown
+# ERR1-NEXT: >>> output section expr: Unknown
+
+# RUN: ld.lld -T %t/a.lds %t/a.o %t/mismatch.o -o %t/mismatch --noinhibit-exec
+# RUN: llvm-readelf -S %t/mismatch | FileCheck %s --check-prefix=MISMATCH
+
+## Mismatched progbits and expr are changed to SHT_PROGBITS.
+# MISMATCH: progbits PROGBITS
+# MISMATCH: note NOTE
+# MISMATCH: expr PROGBITS
+
+# RUN: not ld.lld -T %t/unknown1.lds %t/a.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNKNOWN1
+# UNKNOWN1: error: {{.*}}.lds:1: symbol not found: foo
+
+## For a symbol named SHT_*, give a better diagnostic.
+# RUN: not ld.lld -T %t/unknown2.lds %t/a.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=UNKNOWN2
+# UNKNOWN2: error: {{.*}}.lds:1: unknown section type SHT_DYNAMIC
+
+# RUN: not ld.lld -T %t/parseerr1.lds %t/a.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=PARSEERR1
+# PARSEERR1: error: {{.*}}.lds:1: = expected, but got )
+
+#--- a.s
+.globl _start, myinit
+_start:
+ ret
+myinit:
+ ret
+
+## Compatible with TYPE = SHT_NOTE below.
+.section note,"a", at note
+.byte 0
+
+#--- a.lds
+SECTIONS {
+ progbits (TYPE=SHT_PROGBITS) : { BYTE(1) }
+ note (TYPE = SHT_NOTE) : { BYTE(7) *(note) }
+ nobits ( TYPE=SHT_NOBITS) : { BYTE(8) }
+ init_array (TYPE=SHT_INIT_ARRAY ) : { QUAD(myinit) }
+ fini_array (TYPE=SHT_FINI_ARRAY) : { QUAD(15) }
+ preinit_array (TYPE=SHT_PREINIT_ARRAY) : { QUAD(16) }
+ group (TYPE=17) : { LONG(17) }
+ expr (TYPE=0x41+1) : { BYTE(0x42) *(expr) }
+}
+
+#--- mismatch.s
+.section progbits,"a", at note
+.byte 0
+
+.section expr,"a", at 12345
+.byte 0
+
+#--- unknown1.lds
+SECTIONS { err (TYPE=foo) : {} }
+
+#--- unknown2.lds
+SECTIONS { err (TYPE=SHT_DYNAMIC) : {} }
+
+#--- parseerr1.lds
+SECTIONS { err (TYPE) : {} }
diff --git a/lld/test/ELF/linkerscript/noload.s b/lld/test/ELF/linkerscript/noload.s
index 76007e911b7da..92afadc9b263f 100644
--- a/lld/test/ELF/linkerscript/noload.s
+++ b/lld/test/ELF/linkerscript/noload.s
@@ -1,6 +1,7 @@
# REQUIRES: x86
# RUN: split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/asm -o %t.o
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/mismatch.s -o %t/mismatch.o
# RUN: ld.lld --script %t/lds %t.o -o %t/out
# RUN: llvm-readelf -S -l %t/out | FileCheck %s
@@ -16,16 +17,24 @@
# CHECK: 00 .data_noload_a .data_noload_b .no_input_sec_noload {{$}}
# CHECK: 01 .text {{$}}
+# RUN: not ld.lld --script %t/lds %t.o %t/mismatch.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=ERR
+
+# ERR: error: section type mismatch for .data_noload_a
+
#--- asm
.section .text,"ax", at progbits
nop
-.section .data_noload_a,"aw", at progbits
+.section .data_noload_a,"aw", at nobits
.zero 4096
-.section .data_noload_b,"aw", at progbits
+.section .data_noload_b,"aw", at nobits
.zero 4096
+#--- mismatch.s
+.section .data_noload_a,"aw", at progbits
+.byte 1
+
#--- lds
SECTIONS {
.data_noload_a (NOLOAD) : { *(.data_noload_a) }
More information about the llvm-commits
mailing list