Index: lld/ELF/Config.h =================================================================== --- lld/ELF/Config.h +++ lld/ELF/Config.h @@ -154,6 +154,7 @@ bool demangle = true; bool dependentLibraries; bool disableVerify; + bool dwarf32BeforeDwarf64; bool ehFrameHdr; bool emitLLVM; bool emitRelocs; Index: lld/ELF/Driver.cpp =================================================================== --- lld/ELF/Driver.cpp +++ lld/ELF/Driver.cpp @@ -357,6 +357,9 @@ error("-r and --export-dynamic may not be used together"); } + if (config->dwarf32BeforeDwarf64 && !config->is64) + error("--dwarf32-before-dwarf64 is only supported on 64-bit targets"); + if (config->executeOnly) { if (config->emachine != EM_AARCH64) error("-execute-only is only supported on AArch64 targets"); @@ -969,6 +972,8 @@ errorHandler().errorHandlingScript = args.getLastArgValue(OPT_error_handling_script); + config->dwarf32BeforeDwarf64 = args.hasFlag( + OPT_dwarf32_before_dwarf64, OPT_no_dwarf32_before_dwarf64, false); config->executeOnly = args.hasFlag(OPT_execute_only, OPT_no_execute_only, false); config->exportDynamic = @@ -2029,6 +2034,79 @@ return ret; } +template static bool isDwarf64SectionAux(InputSectionBase *sec) { + if (sec->areRelocsRela) { + auto &r = sec->relas().front(); + return r.getType(config->isMips64EL) == target->symbolicRel; + } else { + auto &r = sec->rels().front(); + return r.getType(config->isMips64EL) == target->symbolicRel; + } +} + +static bool isDwarf64Section(InputSectionBase *sec) { + if (sec->numRelocations == 0) + return false; + switch (config->ekind) { + case ELF64LEKind: + return isDwarf64SectionAux(sec); + case ELF64BEKind: + return isDwarf64SectionAux(sec); + default: + llvm_unreachable("32-bit is unsupported"); + } +} + +static void partitionDwarf32AndDwarf64() { + if (!config->dwarf32BeforeDwarf64) + return; + + // Find input files with DWARF64 debug info. + // The heuristic is that the whole debugging information in the file uses the + // same format, DWARF32 or DWARF64. The first relocation in a .debug_info + // section points to the corresponding record in .debug_abbrev and is + // different for DWARF32 and DWARF64, so it can be used to assume the format. + DenseSet dwarf64Files; + bool dwarf32FileFound = false; + for (BaseCommand *base : script->sectionCommands) { + auto *sec = dyn_cast(base); + if (!sec || (sec->flags & SHF_ALLOC) || sec->name != ".debug_info") + continue; + for (BaseCommand *base : sec->sectionCommands) { + auto *cmd = dyn_cast(base); + if (!cmd) + continue; + for (InputSectionBase *s : cmd->sectionBases) { + if (isDwarf64Section(s)) + dwarf64Files.insert(s->file); + else + dwarf32FileFound = true; + } + } + } + if (dwarf64Files.empty() || !dwarf32FileFound) + return; + + // Partition output debug sections so that input sections from files which + // assumed to be DWARF64 come after others. We suppose that the linker script + // descriptions for output debug sections are not overcomplicated and + // partitioning sections within input section descriptions is enough. + for (BaseCommand *base : script->sectionCommands) { + auto *sec = dyn_cast(base); + if (!sec || (sec->flags & SHF_ALLOC) || !sec->name.startswith(".debug_")) + continue; + for (BaseCommand *base : sec->sectionCommands) { + auto *cmd = dyn_cast(base); + if (!cmd) + continue; + std::stable_partition(cmd->sectionBases.begin(), cmd->sectionBases.end(), + [&](const InputSectionBase *sec) { + return !dwarf64Files.contains(sec->file); + }); + } + } +} + // Do actual linking. Note that when this function is called, // all linker scripts have already been parsed. template void LinkerDriver::link(opt::InputArgList &args) { @@ -2336,6 +2414,8 @@ script->addOrphanSections(); } + partitionDwarf32AndDwarf64(); + { llvm::TimeTraceScope timeScope("Merge/finalize input sections"); Index: lld/ELF/Options.td =================================================================== --- lld/ELF/Options.td +++ lld/ELF/Options.td @@ -150,6 +150,10 @@ def discard_none: F<"discard-none">, HelpText<"Keep all symbols in the symbol table">; +defm dwarf32_before_dwarf64: BB<"dwarf32-before-dwarf64", + "Sort DWARF32 sections before DWARF64 sections", + "Do not sort DWARF32 sections before DWARF64 sections (default)">; + defm dynamic_linker: Eq<"dynamic-linker", "Which dynamic linker to use">; defm dynamic_list : Eq<"dynamic-list", Index: lld/test/ELF/dwarf32-before-dwarf64.s =================================================================== --- /dev/null +++ lld/test/ELF/dwarf32-before-dwarf64.s @@ -0,0 +1,114 @@ +# REQUIRES: x86 +## Test --dwarf32-before-dwarf64 can order DWARF32 input sections before DWARF64 +## input sections to mitigate out-of-range relocations from DWARF32 input sections. + +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/dw32 -o %t32.o +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/dw64 -o %t64.o + +## By default sections are combined in input order. +# RUN: ld.lld %t32.o %t64.o --section-start .text=0x100000 -o %tout +# RUN: llvm-objdump -s -t %tout | FileCheck %s --check-prefixes=CHECK,3264 +# RUN: ld.lld %t64.o %t32.o --section-start .text=0x100000 -o %tout +# RUN: llvm-objdump -s -t %tout | FileCheck %s --check-prefixes=CHECK,6432 + +## --dwarf32-before-dwarf64 orders DWARF32 before DWARF64. +# RUN: ld.lld --dwarf32-before-dwarf64 %t32.o %t64.o --section-start .text=0x100000 -o %tout +# RUN: llvm-objdump -s -t %tout | FileCheck %s --check-prefixes=CHECK,3264 +# RUN: ld.lld --dwarf32-before-dwarf64 %t64.o %t32.o --section-start .text=0x100000 -o %tout +# RUN: llvm-objdump -s -t %tout | FileCheck %s --check-prefixes=CHECK,SORT6432 + +## --no-dwarf32-before-dwarf64 restores the default order. +# RUN: ld.lld --dwarf32-before-dwarf64 --no-dwarf32-before-dwarf64 %t64.o %t32.o --section-start .text=0x100000 -o %tout +# RUN: llvm-objdump -s -t %tout | FileCheck %s --check-prefixes=CHECK,6432 + +## The feature works the same way if debug info sections are defined by a script. +# RUN: ld.lld --dwarf32-before-dwarf64 -T %t/lds %t64.o %t32.o --section-start .text=0x100000 -o %tout +# RUN: llvm-objdump -s -t %tout | FileCheck %s --check-prefixes=CHECK,SORT6432 + +## The order in an executable section follows the input and is not affected +## by the switch. +# CHECK: SYMBOL TABLE: +# 3264-NEXT: 0000000000100000 {{.*}} _start +# 3264-NEXT: 0000000000100004 {{.*}} foo +# 6432-NEXT: 0000000000100000 {{.*}} foo +# 6432-NEXT: 0000000000100004 {{.*}} _start +# SORT6432-NEXT: 0000000000100000 {{.*}} foo +# SORT6432-NEXT: 0000000000100004 {{.*}} _start + +## All debug sections in one input file are reckoned to have the same format, +## DWARF32 or DWARF64, depending on the corresponding .debug_info section. +# CHECK: Contents of section .debug_abbrev: +# 3264-NEXT: 0000 2040 +# 6432-NEXT: 0000 4020 +# SORT6432-NEXT: 0000 2040 +# CHECK: Contents of section .debug_info: +# 3264-NEXT: 0000 20000000 00000000 00001000 00000000 +# 3264-NEXT: 0010 40000000 01000000 00000000 04001000 +# 3264-NEXT: 0020 00000000 +# 6432-NEXT: 0000 40000000 00000000 00000000 00001000 +# 6432-NEXT: 0010 00000000 20000000 01000000 04001000 +# 6432-NEXT: 0020 00000000 +# SORT3264-NEXT: 0000 20000000 00000000 04001000 00000000 +# SORT3264-NEXT: 0010 40000000 01000000 00000000 00001000 +# SORT3264-NEXT: 0020 00000000 + +## Non-debug non-allocatable sections are not sorted by the flag and follow +## the input order. +# CHECK: Contents of section .nondebug: +# 3264-NEXT: 0000 2040 +# 6432-NEXT: 0000 4020 +# SORT6432-NEXT: 0000 4020 + +## The flag cannot be used on 32-bit targets. +# RUN: llvm-mc -filetype=obj -triple=i386 /dev/null -o %t.o +# RUN: not ld.lld --dwarf32-before-dwarf64 %t.o -o /dev/null 2>&1 | \ +# RUN: FileCheck %s --check-prefix=32BIT + +# 32BIT: error: --dwarf32-before-dwarf64 is only supported on 64-bit targets + +#--- dw32 +.globl _start +_start: + nop + +## The linker does not analyze the content of debug sections, at least in +## the scenarios of this test, so we have some fictious values here to help +## distinguish fake DWARF32 sections from DWARF64 ones. +.section .debug_abbrev + .byte 32 + +## The first relocation in a .debug_info section points to a corresponding +## record in .debug_abbrev. It is 32-bit for DWARF32 and 64-bit for DWARF64, +## so it is used to assess the actual format of the debugging information of +## the file. +.section .debug_info,"",@progbits + .long 32 + .long .debug_abbrev + .quad _start + +.section .nondebug,"",@progbits + .byte 32 + +#--- dw64 +.globl foo +foo: + nop + +.section .debug_abbrev + .byte 64 + +.section .debug_info,"",@progbits + .long 64 + .quad .debug_abbrev + .quad foo + +.section .nondebug,"",@progbits + .byte 64 + +#--- lds +SECTIONS { + .debug_abbrev : { *(.debug_abbrev) } + .debug_info : { *(.debug_info) } + .nondebug : { *(.nondebug) } +}