diff --git a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp --- a/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp +++ b/llvm/lib/CodeGen/TargetLoweringObjectFileImpl.cpp @@ -507,6 +507,32 @@ return Flags; } +/// Return true if one of the default sections +static bool isDefaultELFSection(const MCSectionELF &S) { + for (StringRef prefix : + {".bss", ".text", ".data", ".rodata", ".tdata", ".tbss", + ".gcc_except_table", ".debug_", ".apple_", ".llvm_", ".eh_frame", + ".stack_sizes", ".pseudo_probe"}) + if (S.getName().startswith(prefix)) + return true; + return false; +} + +/// Return true if the section is compatible with the given flags. +static bool flagsCompatible(const unsigned OldFlags, const unsigned NewFlags) { + // WRITE and EXECINSTR are not compatible + if (OldFlags & ELF::SHF_EXECINSTR && NewFlags & ELF::SHF_WRITE) + return false; + if (OldFlags & ELF::SHF_WRITE && NewFlags & ELF::SHF_EXECINSTR) + return false; + + // TLS and non-TLS are not compatible + if ((OldFlags & ELF::SHF_TLS) != (NewFlags & ELF::SHF_TLS)) + return false; + + return true; +} + static const Comdat *getELFComdat(const GlobalValue *GV) { const Comdat *C = GV->getComdat(); if (!C) @@ -726,6 +752,28 @@ assert(Section->getLinkedToSymbol() == LinkedToSym && "Associated symbol mismatch between sections"); + // Update the seen flags of user defined sections, raise an error if invalid + // combinations are seen. Don't update flags of standard sections (.text etc). + if (Section->getFlags() != Flags && !isDefaultELFSection(*Section)) { + if (flagsCompatible(Section->getFlags(), Flags)) { + // If all flags are compatible then add any new ones to the section. + const auto MergedFlags = Section->getFlags() | Flags; + if (MergedFlags != Section->getFlags()) { + Section->setFlags(MergedFlags); + GO->getContext().diagnose(LoweringDiagnosticInfo( + "Symbol " + GO->getName() + ": updating section flags: " + + SectionName + " 0x" + utohexstr(MergedFlags), + DS_Remark)); + } + } else { + GO->getContext().diagnose(LoweringDiagnosticInfo( + "Symbol '" + GO->getName() + + "' implies incompatible flags for section '" + SectionName + + "'. Old: 0x" + utohexstr(Section->getFlags()) + ", New: 0x" + + utohexstr(Flags))); + } + } + if (!getContext().getAsmInfo()->useIntegratedAssembler()) { // If we are not using the integrated assembler then this symbol might have // been placed in an incompatible mergeable section. Emit an error if this diff --git a/llvm/test/CodeGen/Generic/elf-incompatible-section-flags.ll b/llvm/test/CodeGen/Generic/elf-incompatible-section-flags.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Generic/elf-incompatible-section-flags.ll @@ -0,0 +1,55 @@ +; RUN: not llc -mtriple=aarch64-none-eabi %s -o - 2>&1 | FileCheck %s +; RUN: not llc -mtriple=aarch64-none-eabi %s -o - 2> /dev/null | FileCheck %s --check-prefix=ASSEMBLY + +; User creates globals with SHF_WRITE and SHF_EXECINSTR +@rw_s1 = global i32 10, section "s1_ax", align 4 +define i32 @fn_s1() section "s1_ax" { + entry: + ret i32 0 +} +; CHECK: error: Symbol 'rw_s1' implies incompatible flags for section 's1_ax'. Old: 0x6, New: 0x3 + +; This is fine; constant can live in SHF_EXECINSTR section +@ro_s2 = constant i32 10, section "s2_ax", align 4 +define i32 @fn_s2() section "s2_ax" { + entry: + ret i32 0 +} +; CHECK-NOT: error: Symbol 'ro_s2' implies incompatible flags for section 's2_ax'. +; CHECK-NOT: remark: Symbol ro_s2: updating section flags +; ASSEMBLY: .section s2_ax,"ax" +; ASSEMBLY: .section s2_ax,"ax" + +; This is also fine; constant can live in SHF_WRITE +@ro_s3 = constant i32 10, section "s3_aw", align 4 +@rw_s3 = global i32 10, section "s3_aw", align 4 +; CHECK-NOT: error: Symbol 'rw_s3' implies incompatible flags for section 's3_aw'. +; CHECK: remark: Symbol rw_s3: updating section flags: s3_aw 0x3 +; ASSEMBLY: .section s3_aw,"a" + +; This is fine; .text is read only, but the flags shouldn't change +@ro_text = constant i32 10, section ".text", align 4 +; CHECK-NOT: error: Symbol 'ro_text' implies incompatible flags for section '.text'. +; CHECK-NOT: remark: Symbol ro_text: updating section flags: .text + +; This is fine; .text is read only, but mutable globals are not necessarily written to +@rw_text = global i32 10, section ".text", align 4 +; CHECK-NOT: error: Symbol 'rw_text' implies incompatible flags for section '.text'. Old: 0x6, New: 0x3 +; CHECK-NOT: remark: Symbol rw_text: updating section flags: .text + +; This is fine; .data is writeable, but the flags shouldn't change and constants are allowed +@ro_data = constant i32 10, section ".data", align 4 +; CHECK-NOT: error: Symbol 'ro_data' implies incompatible flags for section '.data'. +; CHECK-NOT: remark: Symbol ro_data: updating section flags: .data + +; TLS and non-TLS symbols cannot live in the same section +@tls_var = thread_local global i32 10, section "s4", align 4 +@non_tls_var = global i32 10, section "s4", align 4 +; CHECK: error: Symbol 'non_tls_var' implies incompatible flags for section 's4'. Old: 0x403, New: 0x3 +; CHECK-NOT: remark: Symbol non_tls_var: updating section flags: s4 + +; TLS and non-TLS symbols cannot live in the same section +@non_tls_var2 = global i32 10, section "s5", align 4 +@tls_var2 = thread_local global i32 10, section "s5", align 4 +; CHECK: error: Symbol 'tls_var2' implies incompatible flags for section 's5'. Old: 0x3, New: 0x403 +; CHECK-NOT: remark: Symbol tls_var2: updating section flags: s5