diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "Writer.h" +#include "../../llvm/tools/llvm-readobj/ELFDumper.h" #include "AArch64ErrataFix.h" #include "ARMErrataFix.h" #include "CallGraphSort.h" @@ -24,6 +25,7 @@ #include "lld/Common/Strings.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/FormattedStream.h" #include "llvm/Support/Parallel.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/SHA1.h" @@ -578,6 +580,71 @@ add(in.strTab); } +struct Field { + std::string Str; + unsigned Column; + + Field(StringRef S, unsigned Col) : Str(std::string(S)), Column(Col) {} + Field(unsigned Col) : Column(Col) {} +}; + +static void printField(struct Field F, formatted_raw_ostream &OS) { + if (F.Column != 0) + OS.PadToColumn(F.Column); + OS << F.Str; + OS.flush(); + ; +} + +void static printSectionInfo() { + if (!errorHandler().verbose) + return; + unsigned Bias = config->is64 ? 0 : 8; + ScopedPrinter W(fouts()); + auto &OS = static_cast(W.getOStream()); + OS << "Section Headers:\n"; + OS.flush(); + + Field Fields[11] = { + {"[Nr]", 2}, {"Name", 7}, {"Type", 25}, + {"Address", 41}, {"Off", 58 - Bias}, {"Size", 65 - Bias}, + {"ES", 72 - Bias}, {"Flg", 75 - Bias}, {"Lk", 79 - Bias}, + {"Inf", 82 - Bias}, {"Al", 86 - Bias}}; + + for (const auto &F : Fields) { + printField(F, OS); + } + OS << "\n"; + for (const OutputSection *out : outputSections) { + Fields[0].Str = to_string(out->sectionIndex); + Fields[1].Str = out->name.str(); + Fields[2].Str = getSectionTypeString(config->emachine, out->type); + Fields[3].Str = + to_string(format_hex_no_prefix(out->addr, config->is64 ? 16 : 8)); + Fields[4].Str = to_string(format_hex_no_prefix(out->offset, 6)); + Fields[5].Str = to_string(format_hex_no_prefix(out->size, 6)); + Fields[6].Str = to_string(format_hex_no_prefix(out->entsize, 2)); + Fields[7].Str = getGNUFlags(config->emachine, out->flags); + Fields[8].Str = to_string(out->link); + Fields[9].Str = to_string(out->info); + Fields[10].Str = to_string(out->alignment); + + OS.PadToColumn(Fields[0].Column); + OS << "[" << right_justify(Fields[0].Str, 2) << "]"; + for (int i = 1; i < 7; i++) + printField(Fields[i], OS); + OS.PadToColumn(Fields[7].Column); + OS << right_justify(Fields[7].Str, 3); + OS.PadToColumn(Fields[8].Column); + OS << right_justify(Fields[8].Str, 2); + OS.PadToColumn(Fields[9].Column); + OS << right_justify(Fields[9].Str, 3); + OS.PadToColumn(Fields[10].Column); + OS << right_justify(Fields[10].Str, 2); + OS << "\n"; + } +} + // The main function of the writer. template void Writer::run() { copyLocalSymbols(); @@ -590,6 +657,7 @@ // to the string table, and add entries to .got and .plt. // finalizeSections does that. finalizeSections(); + printSectionInfo(); checkExecuteOnly(); if (errorCount()) return; diff --git a/llvm/tools/llvm-readobj/ELFDumper.h b/llvm/tools/llvm-readobj/ELFDumper.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-readobj/ELFDumper.h @@ -0,0 +1,192 @@ +//===-- ELFDumper.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_READOBJ_ELFDUMPER_H +#define LLVM_TOOLS_LLVM_READOBJ_ELFDUMPER_H + +#include "llvm/Object/ELF.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/ScopedPrinter.h" + +namespace llvm { +namespace ELF { +#define LLVM_READOBJ_ENUM_CASE(ns, enum) \ + case ns::enum: \ + return #enum; + +#define ENUM_ENT(enum, altName) \ + { #enum, altName, ELF::enum } + +#define ENUM_ENT_1(enum) \ + { #enum, #enum, ELF::enum } + +const EnumEntry ElfSectionFlags[] = { + ENUM_ENT(SHF_WRITE, "W"), ENUM_ENT(SHF_ALLOC, "A"), + ENUM_ENT(SHF_EXECINSTR, "X"), ENUM_ENT(SHF_MERGE, "M"), + ENUM_ENT(SHF_STRINGS, "S"), ENUM_ENT(SHF_INFO_LINK, "I"), + ENUM_ENT(SHF_LINK_ORDER, "L"), ENUM_ENT(SHF_OS_NONCONFORMING, "O"), + ENUM_ENT(SHF_GROUP, "G"), ENUM_ENT(SHF_TLS, "T"), + ENUM_ENT(SHF_COMPRESSED, "C"), ENUM_ENT(SHF_EXCLUDE, "E"), +}; + +const EnumEntry ElfXCoreSectionFlags[] = { + ENUM_ENT(XCORE_SHF_CP_SECTION, ""), ENUM_ENT(XCORE_SHF_DP_SECTION, "")}; + +const EnumEntry ElfARMSectionFlags[] = { + ENUM_ENT(SHF_ARM_PURECODE, "y")}; + +const EnumEntry ElfHexagonSectionFlags[] = { + ENUM_ENT(SHF_HEX_GPREL, "")}; + +const EnumEntry ElfMipsSectionFlags[] = { + ENUM_ENT(SHF_MIPS_NODUPES, ""), ENUM_ENT(SHF_MIPS_NAMES, ""), + ENUM_ENT(SHF_MIPS_LOCAL, ""), ENUM_ENT(SHF_MIPS_NOSTRIP, ""), + ENUM_ENT(SHF_MIPS_GPREL, ""), ENUM_ENT(SHF_MIPS_MERGE, ""), + ENUM_ENT(SHF_MIPS_ADDR, ""), ENUM_ENT(SHF_MIPS_STRING, "")}; + +const EnumEntry ElfX86_64SectionFlags[] = { + ENUM_ENT(SHF_X86_64_LARGE, "l")}; + +inline std::vector> +getSectionFlagsForTarget(unsigned EMachine) { + std::vector> Ret(std::begin(ElfSectionFlags), + std::end(ElfSectionFlags)); + switch (EMachine) { + case EM_ARM: + Ret.insert(Ret.end(), std::begin(ElfARMSectionFlags), + std::end(ElfARMSectionFlags)); + break; + case EM_HEXAGON: + Ret.insert(Ret.end(), std::begin(ElfHexagonSectionFlags), + std::end(ElfHexagonSectionFlags)); + break; + case EM_MIPS: + Ret.insert(Ret.end(), std::begin(ElfMipsSectionFlags), + std::end(ElfMipsSectionFlags)); + break; + case EM_X86_64: + Ret.insert(Ret.end(), std::begin(ElfX86_64SectionFlags), + std::end(ElfX86_64SectionFlags)); + break; + case EM_XCORE: + Ret.insert(Ret.end(), std::begin(ElfXCoreSectionFlags), + std::end(ElfXCoreSectionFlags)); + break; + default: + break; + } + return Ret; +} + +inline std::string getGNUFlags(unsigned EMachine, uint64_t Flags) { + // Here we are trying to build the flags string in the same way as GNU does. + // It is not that straightforward. Imagine we have sh_flags == 0x90000000. + // SHF_EXCLUDE ("E") has a value of 0x80000000 and SHF_MASKPROC is 0xf0000000. + // GNU readelf will not print "E" or "Ep" in this case, but will print just + // "p". It only will print "E" when no other processor flag is set. + std::string Str; + bool HasUnknownFlag = false; + bool HasOSFlag = false; + bool HasProcFlag = false; + std::vector> FlagsList = + getSectionFlagsForTarget(EMachine); + while (Flags) { + // Take the least significant bit as a flag. + uint64_t Flag = Flags & -Flags; + Flags -= Flag; + + // Find the flag in the known flags list. + auto I = llvm::find_if(FlagsList, [=](const EnumEntry &E) { + // Flags with empty names are not printed in GNU style output. + return E.Value == Flag && !E.AltName.empty(); + }); + if (I != FlagsList.end()) { + Str += I->AltName; + continue; + } + + // If we did not find a matching regular flag, then we deal with an OS + // specific flag, processor specific flag or an unknown flag. + if (Flag & ELF::SHF_MASKOS) { + HasOSFlag = true; + Flags &= ~ELF::SHF_MASKOS; + } else if (Flag & ELF::SHF_MASKPROC) { + HasProcFlag = true; + // Mask off all the processor-specific bits. This removes the SHF_EXCLUDE + // bit if set so that it doesn't also get printed. + Flags &= ~ELF::SHF_MASKPROC; + } else { + HasUnknownFlag = true; + } + } + + // "o", "p" and "x" are printed last. + if (HasOSFlag) + Str += "o"; + if (HasProcFlag) + Str += "p"; + if (HasUnknownFlag) + Str += "x"; + return Str; +} + +// Print the offset of a particular section from anyone of the ranges: +// [SHT_LOOS, SHT_HIOS], [SHT_LOPROC, SHT_HIPROC], [SHT_LOUSER, SHT_HIUSER]. +// If 'Type' does not fall within any of those ranges, then a string is +// returned as '' followed by the type value. +inline std::string getSectionTypeOffsetString(unsigned Type) { + if (Type >= SHT_LOOS && Type <= SHT_HIOS) + return "LOOS+0x" + to_hexString(Type - SHT_LOOS); + else if (Type >= SHT_LOPROC && Type <= SHT_HIPROC) + return "LOPROC+0x" + to_hexString(Type - SHT_LOPROC); + else if (Type >= SHT_LOUSER && Type <= SHT_HIUSER) + return "LOUSER+0x" + to_hexString(Type - SHT_LOUSER); + return "0x" + to_hexString(Type) + ": "; +} + +inline std::string getSectionTypeString(unsigned Machine, unsigned Type) { + StringRef Name = object::getELFSectionTypeName(Machine, Type); + + // Handle SHT_GNU_* type names. + if (Name.startswith("SHT_GNU_")) { + if (Name == "SHT_GNU_HASH") + return "GNU_HASH"; + // E.g. SHT_GNU_verneed -> VERNEED. + return Name.drop_front(8).upper(); + } + + if (Name == "SHT_SYMTAB_SHNDX") + return "SYMTAB SECTION INDICES"; + + if (Name.startswith("SHT_")) + return Name.drop_front(4).str(); + return getSectionTypeOffsetString(Type); +} + +inline void printSectionDescription(formatted_raw_ostream &OS, + unsigned EMachine) { + OS << "Key to Flags:\n"; + OS << " W (write), A (alloc), X (execute), M (merge), S (strings), I " + "(info),\n"; + OS << " L (link order), O (extra OS processing required), G (group), T " + "(TLS),\n"; + OS << " C (compressed), x (unknown), o (OS specific), E (exclude),\n"; + + if (EMachine == EM_X86_64) + OS << " l (large), "; + else if (EMachine == EM_ARM) + OS << " y (purecode), "; + else + OS << " "; + + OS << "p (processor specific)\n"; +} +} // namespace ELF +} // namespace llvm + +#endif diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -11,6 +11,7 @@ /// //===----------------------------------------------------------------------===// +#include "ELFDumper.h" #include "ARMEHABIPrinter.h" #include "DwarfCFIEHPrinter.h" #include "ObjDumper.h" @@ -31,7 +32,6 @@ #include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Demangle/Demangle.h" -#include "llvm/Object/ELF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" #include "llvm/Object/Error.h" @@ -47,13 +47,11 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/FormattedStream.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MipsABIFlags.h" #include "llvm/Support/RISCVAttributeParser.h" #include "llvm/Support/RISCVAttributes.h" -#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" #include #include @@ -70,16 +68,6 @@ using namespace llvm::object; using namespace ELF; -#define LLVM_READOBJ_ENUM_CASE(ns, enum) \ - case ns::enum: \ - return #enum; - -#define ENUM_ENT(enum, altName) \ - { #enum, altName, ELF::enum } - -#define ENUM_ENT_1(enum) \ - { #enum, #enum, ELF::enum } - namespace { template class DumpStyle; @@ -1510,132 +1498,6 @@ return "(unknown)"; } -static const EnumEntry ElfSectionFlags[] = { - ENUM_ENT(SHF_WRITE, "W"), - ENUM_ENT(SHF_ALLOC, "A"), - ENUM_ENT(SHF_EXECINSTR, "X"), - ENUM_ENT(SHF_MERGE, "M"), - ENUM_ENT(SHF_STRINGS, "S"), - ENUM_ENT(SHF_INFO_LINK, "I"), - ENUM_ENT(SHF_LINK_ORDER, "L"), - ENUM_ENT(SHF_OS_NONCONFORMING, "O"), - ENUM_ENT(SHF_GROUP, "G"), - ENUM_ENT(SHF_TLS, "T"), - ENUM_ENT(SHF_COMPRESSED, "C"), - ENUM_ENT(SHF_EXCLUDE, "E"), -}; - -static const EnumEntry ElfXCoreSectionFlags[] = { - ENUM_ENT(XCORE_SHF_CP_SECTION, ""), - ENUM_ENT(XCORE_SHF_DP_SECTION, "") -}; - -static const EnumEntry ElfARMSectionFlags[] = { - ENUM_ENT(SHF_ARM_PURECODE, "y") -}; - -static const EnumEntry ElfHexagonSectionFlags[] = { - ENUM_ENT(SHF_HEX_GPREL, "") -}; - -static const EnumEntry ElfMipsSectionFlags[] = { - ENUM_ENT(SHF_MIPS_NODUPES, ""), - ENUM_ENT(SHF_MIPS_NAMES, ""), - ENUM_ENT(SHF_MIPS_LOCAL, ""), - ENUM_ENT(SHF_MIPS_NOSTRIP, ""), - ENUM_ENT(SHF_MIPS_GPREL, ""), - ENUM_ENT(SHF_MIPS_MERGE, ""), - ENUM_ENT(SHF_MIPS_ADDR, ""), - ENUM_ENT(SHF_MIPS_STRING, "") -}; - -static const EnumEntry ElfX86_64SectionFlags[] = { - ENUM_ENT(SHF_X86_64_LARGE, "l") -}; - -static std::vector> -getSectionFlagsForTarget(unsigned EMachine) { - std::vector> Ret(std::begin(ElfSectionFlags), - std::end(ElfSectionFlags)); - switch (EMachine) { - case EM_ARM: - Ret.insert(Ret.end(), std::begin(ElfARMSectionFlags), - std::end(ElfARMSectionFlags)); - break; - case EM_HEXAGON: - Ret.insert(Ret.end(), std::begin(ElfHexagonSectionFlags), - std::end(ElfHexagonSectionFlags)); - break; - case EM_MIPS: - Ret.insert(Ret.end(), std::begin(ElfMipsSectionFlags), - std::end(ElfMipsSectionFlags)); - break; - case EM_X86_64: - Ret.insert(Ret.end(), std::begin(ElfX86_64SectionFlags), - std::end(ElfX86_64SectionFlags)); - break; - case EM_XCORE: - Ret.insert(Ret.end(), std::begin(ElfXCoreSectionFlags), - std::end(ElfXCoreSectionFlags)); - break; - default: - break; - } - return Ret; -} - -static std::string getGNUFlags(unsigned EMachine, uint64_t Flags) { - // Here we are trying to build the flags string in the same way as GNU does. - // It is not that straightforward. Imagine we have sh_flags == 0x90000000. - // SHF_EXCLUDE ("E") has a value of 0x80000000 and SHF_MASKPROC is 0xf0000000. - // GNU readelf will not print "E" or "Ep" in this case, but will print just - // "p". It only will print "E" when no other processor flag is set. - std::string Str; - bool HasUnknownFlag = false; - bool HasOSFlag = false; - bool HasProcFlag = false; - std::vector> FlagsList = - getSectionFlagsForTarget(EMachine); - while (Flags) { - // Take the least significant bit as a flag. - uint64_t Flag = Flags & -Flags; - Flags -= Flag; - - // Find the flag in the known flags list. - auto I = llvm::find_if(FlagsList, [=](const EnumEntry &E) { - // Flags with empty names are not printed in GNU style output. - return E.Value == Flag && !E.AltName.empty(); - }); - if (I != FlagsList.end()) { - Str += I->AltName; - continue; - } - - // If we did not find a matching regular flag, then we deal with an OS - // specific flag, processor specific flag or an unknown flag. - if (Flag & ELF::SHF_MASKOS) { - HasOSFlag = true; - Flags &= ~ELF::SHF_MASKOS; - } else if (Flag & ELF::SHF_MASKPROC) { - HasProcFlag = true; - // Mask off all the processor-specific bits. This removes the SHF_EXCLUDE - // bit if set so that it doesn't also get printed. - Flags &= ~ELF::SHF_MASKPROC; - } else { - HasUnknownFlag = true; - } - } - - // "o", "p" and "x" are printed last. - if (HasOSFlag) - Str += "o"; - if (HasProcFlag) - Str += "p"; - if (HasUnknownFlag) - Str += "x"; - return Str; -} - static StringRef segmentTypeToString(unsigned Arch, unsigned Type) { // Check potentially overlapped processor-specific program header type. switch (Arch) { @@ -3835,58 +3697,6 @@ OS << "\nThere are no relocations in this file.\n"; } -// Print the offset of a particular section from anyone of the ranges: -// [SHT_LOOS, SHT_HIOS], [SHT_LOPROC, SHT_HIPROC], [SHT_LOUSER, SHT_HIUSER]. -// If 'Type' does not fall within any of those ranges, then a string is -// returned as '' followed by the type value. -static std::string getSectionTypeOffsetString(unsigned Type) { - if (Type >= SHT_LOOS && Type <= SHT_HIOS) - return "LOOS+0x" + to_hexString(Type - SHT_LOOS); - else if (Type >= SHT_LOPROC && Type <= SHT_HIPROC) - return "LOPROC+0x" + to_hexString(Type - SHT_LOPROC); - else if (Type >= SHT_LOUSER && Type <= SHT_HIUSER) - return "LOUSER+0x" + to_hexString(Type - SHT_LOUSER); - return "0x" + to_hexString(Type) + ": "; -} - -static std::string getSectionTypeString(unsigned Machine, unsigned Type) { - StringRef Name = getELFSectionTypeName(Machine, Type); - - // Handle SHT_GNU_* type names. - if (Name.startswith("SHT_GNU_")) { - if (Name == "SHT_GNU_HASH") - return "GNU_HASH"; - // E.g. SHT_GNU_verneed -> VERNEED. - return Name.drop_front(8).upper(); - } - - if (Name == "SHT_SYMTAB_SHNDX") - return "SYMTAB SECTION INDICES"; - - if (Name.startswith("SHT_")) - return Name.drop_front(4).str(); - return getSectionTypeOffsetString(Type); -} - -static void printSectionDescription(formatted_raw_ostream &OS, - unsigned EMachine) { - OS << "Key to Flags:\n"; - OS << " W (write), A (alloc), X (execute), M (merge), S (strings), I " - "(info),\n"; - OS << " L (link order), O (extra OS processing required), G (group), T " - "(TLS),\n"; - OS << " C (compressed), x (unknown), o (OS specific), E (exclude),\n"; - - if (EMachine == EM_X86_64) - OS << " l (large), "; - else if (EMachine == EM_ARM) - OS << " y (purecode), "; - else - OS << " "; - - OS << "p (processor specific)\n"; -} - template void GNUStyle::printSectionHeaders() { unsigned Bias = ELFT::Is64Bits ? 0 : 8; ArrayRef Sections = cantFail(this->Obj.sections()); diff --git a/llvm/tools/llvm-readobj/ObjDumper.h b/llvm/tools/llvm-readobj/ObjDumper.h --- a/llvm/tools/llvm-readobj/ObjDumper.h +++ b/llvm/tools/llvm-readobj/ObjDumper.h @@ -15,6 +15,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/FormattedStream.h" #include