diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h --- a/lld/ELF/Config.h +++ b/lld/ELF/Config.h @@ -173,6 +173,7 @@ llvm::StringRef optStatsFilename; llvm::StringRef progName; llvm::StringRef printArchiveStats; + llvm::StringRef printRelocationStats; llvm::StringRef printSymbolOrder; llvm::StringRef soName; llvm::StringRef sysroot; diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -1267,6 +1267,8 @@ args.hasFlag(OPT_print_gc_sections, OPT_no_print_gc_sections, false); config->printMemoryUsage = args.hasArg(OPT_print_memory_usage); config->printArchiveStats = args.getLastArgValue(OPT_print_archive_stats); + config->printRelocationStats = + args.getLastArgValue(OPT_print_relocation_stats); config->printSymbolOrder = args.getLastArgValue(OPT_print_symbol_order); config->relax = args.hasFlag(OPT_relax, OPT_no_relax, true); diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td --- a/lld/ELF/Options.td +++ b/lld/ELF/Options.td @@ -353,6 +353,9 @@ HelpText<"Write archive usage statistics to the specified file. " "Print the numbers of members and extracted members for each archive">; +defm print_relocation_stats: Eq<"print-relocation-stats", + "Print relocation statistics into the specified file">; + defm print_symbol_order: Eq<"print-symbol-order", "Print a symbol order specified by --call-graph-ordering-file into the specified file">; diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -246,10 +246,30 @@ void reportRangeError(uint8_t *loc, int64_t v, int n, const Symbol &sym, const Twine &msg); +LLVM_LIBRARY_VISIBILITY inline std::atomic min32Offset = 0, + max32Offset = 0; +LLVM_LIBRARY_VISIBILITY inline std::mutex min32OffsetMu, max32OffsetMu; + // Make sure that V can be represented as an N bit signed integer. inline void checkInt(uint8_t *loc, int64_t v, int n, const Relocation &rel) { if (v != llvm::SignExtend64(v, n)) reportRangeError(loc, rel, Twine(v), llvm::minIntN(n), llvm::maxIntN(n)); + // If we request relocation stats, perform double checked locking to keep + // track of the min/max 32-bit signed offset we encounter. + if (n == 32 && !config->printRelocationStats.empty()) { + int64_t lo = min32Offset.load(); + if (v < lo) { + std::lock_guard l(min32OffsetMu); + if (v < min32Offset) + min32Offset = v; + } + int64_t hi = max32Offset.load(); + if (v > hi) { + std::lock_guard l(max32OffsetMu); + if (v > max32Offset) + max32Offset = v; + } + } } // Make sure that V can be represented as an N bit unsigned integer. diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -29,6 +29,7 @@ #include "llvm/Support/Parallel.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/TimeProfiler.h" +#include "llvm/Support/raw_ostream.h" #include "llvm/Support/xxhash.h" #include @@ -79,6 +80,7 @@ void writeSections(); void writeSectionsBinary(); void writeBuildId(); + void writeStats(); std::unique_ptr &buffer; @@ -599,6 +601,7 @@ // Backfill .note.gnu.build-id section content. This is done at last // because the content is usually a hash value of the entire output file. writeBuildId(); + writeStats(); if (errorCount()) return; @@ -3024,6 +3027,19 @@ part.buildId->writeBuildId(output); } +template void Writer::writeStats() { + if (config->printRelocationStats.empty()) + return; + std::error_code ec; + llvm::raw_fd_ostream out(config->printRelocationStats, ec); + if (ec) { + error("Could not open file to write stats: " + ec.message()); + return; + } + out << "Min 32-bit offset: " << min32Offset << "\n"; + out << "Max 32-bit offset: " << max32Offset << "\n"; +} + template void elf::createSyntheticSections(); template void elf::createSyntheticSections(); template void elf::createSyntheticSections(); diff --git a/lld/test/ELF/print-relocation-stats.s b/lld/test/ELF/print-relocation-stats.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/print-relocation-stats.s @@ -0,0 +1,27 @@ +# REQUIRES: x86 +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64 %t/a.s -o %t/a.o +# RUN: ld.lld -T %t/lds %t/a.o -o /dev/null --print-relocation-stats=%t/stats +# RUN: FileCheck %s --input-file %t/stats + +# CHECK: Min 32-bit offset: -99999 +# CHECK: Max 32-bit offset: 199994 + +#--- a.s +.section data1,"aw",@progbits +.space 8 + +.section .text, "ax", @progbits + movq __stop_data1@GOTPCREL(%rip), %rax + movq __stop_data2@GOTPCREL(%rip), %rax + +.section data2,"aw",@progbits +.space 8 + +#--- lds +SECTIONS { + data1 100000 : { *(data1) } + .text 200000 : { *(.text) } + data2 400000 : { *(data2) } +} +