diff --git a/llvm/test/tools/llvm-nm/XCOFF/basic.test b/llvm/test/tools/llvm-nm/XCOFF/basic.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/XCOFF/basic.test @@ -0,0 +1,104 @@ +## Test llvm-nm for XCOFF object files. +## Test: local symbol in .text section, show type char 't' +## Test: local symbol in .data section, show type char 'd' +## Test: symbol in .bss section, show type char 'b' +## Test: symbol in .debug section, show type char 'N' +## Test: symbol .file show tpye char 'f' +## Test: global symbol in .text section, show type char 'T' +## Test: global symbol in .data section, show type char 'D' +## Test: global weak symbol show type char 'W' +## Test: common symbol show type char 'C' +## Test: Undefined symbol show type char 'U' + +# RUN: yaml2obj --docnum=1 %s -o %t.o +# RUN: llvm-nm %t.o | FileCheck --check-prefix=NM-SYM --match-full-lines %s + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text + Address: 0x0 + Flags: [ STYP_TEXT ] + - Name: .data + Flags: [ STYP_DATA ] + - Name: .bss + Flags: [ STYP_BSS ] + - Name: .debug + Flags: [ STYP_DEBUG ] +Symbols: + - Name: .file + Section: N_DEBUG + StorageClass: C_FILE + - Name: .text + Value: 0x10 + Section: .text + StorageClass: C_STAT + NumberOfAuxEntries: 1 + - Name: .data + Value: 0x80 + Section: .data + StorageClass: C_STAT + NumberOfAuxEntries: 1 + - Name: .bss + Value: 0x310 + Section: .bss + StorageClass: C_STAT + - Name: .debug + Section: .debug + StorageClass: C_STAT + - Name: ._ZL5func0v + Section: .text + StorageClass: C_EXT + Type: 0x20 + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_PR + - Name: ._Z3fwpv + Section: .text + StorageClass: C_WEAKEXT + Type: 0x20 + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x02 + StorageMappingClass: XMC_PR + - Name: val + Section: .data + StorageClass: C_EXT + Type: 0x20 + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x01 + StorageMappingClass: XMC_RW + SectionOrLength: 0x4 + - Name: extval + Section: N_UNDEF + StorageClass: C_EXT + Type: 0x00 + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x01 + StorageMappingClass: XMC_UA + SectionOrLength: 0x0 + - Name: comval + Section: .bss + Value: 0x13C + StorageClass: C_EXT + Type: 0x00 + AuxEntries: + - Type: AUX_CSECT + SymbolAlignmentAndType: 0x03 + StorageMappingClass: XMC_RW + SectionOrLength: 0x0 + +# NM-SYM: 00000000 W ._Z3fwpv +# NM-SYM-NEXT: 00000000 T ._ZL5func0v +# NM-SYM-NEXT: 00000310 b .bss +# NM-SYM-NEXT: 00000080 d .data +# NM-SYM-NEXT: 00000000 N .debug +# NM-SYM-NEXT: 00000000 f .file +# NM-SYM-NEXT: 00000010 t .text +# NM-SYM-NEXT: 0000013c C comval +# NM-SYM-NEXT: U extval +# NM-SYM-NEXT: 00000000 D val diff --git a/llvm/test/tools/llvm-nm/XCOFF/invalid-section-index.test b/llvm/test/tools/llvm-nm/XCOFF/invalid-section-index.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-nm/XCOFF/invalid-section-index.test @@ -0,0 +1,20 @@ +## Test the behavior of the symbol reference section. + +# RUN: yaml2obj --docnum=1 %s -o %t.o +# RUN: llvm-nm %t.o 2>&1 | FileCheck %s -DFILE=%t.o --check-prefix=NM + +# NM: {{.*}}llvm-nm{{(\.exe)?}}: warning: [[FILE]]: for symbol with index 0: the section index (4) is invalid +# NM-NEXT: 00000000 ? .text + +--- !XCOFF +FileHeader: + MagicNumber: 0x1DF +Sections: + - Name: .text +Symbols: + - Name: .text + Value: 0x0 + SectionIndex: 4 + Type: 0x0 + StorageClass: C_STAT + NumberOfAuxEntries: 1 diff --git a/llvm/tools/llvm-nm/llvm-nm.cpp b/llvm/tools/llvm-nm/llvm-nm.cpp --- a/llvm/tools/llvm-nm/llvm-nm.cpp +++ b/llvm/tools/llvm-nm/llvm-nm.cpp @@ -31,6 +31,7 @@ #include "llvm/Object/TapiFile.h" #include "llvm/Object/TapiUniversal.h" #include "llvm/Object/Wasm.h" +#include "llvm/Object/XCOFFObjectFile.h" #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/Option.h" @@ -127,6 +128,22 @@ static StringRef ToolName; +static void warn(Error Err, Twine FileName, Twine Context = Twine(), + Twine Archive = Twine()) { + assert(Err); + + // Flush the standard output so that the warning isn't interleaved with other + // output if stdout and stderr are writing to the same place. + outs().flush(); + + handleAllErrors(std::move(Err), [&](const ErrorInfoBase &EI) { + WithColor::warning(errs(), ToolName) + << (Archive.str().empty() ? FileName : Archive + "(" + FileName + ")") + << ": " << (Context.str().empty() ? "" : Context + ": ") << EI.message() + << "\n"; + }); +} + static void error(Twine Message, Twine Path = Twine()) { HadError = true; WithColor::error(errs(), ToolName) << Path << ": " << Message << "\n"; @@ -246,6 +263,9 @@ return Triple(IRObj->getTargetTriple()).isArch64Bit(); if (isa(Obj) || isa(Obj)) return false; + if (XCOFFObjectFile *XCOFFObj = dyn_cast(&Obj)) + return XCOFFObj->is64Bit(); + if (isa(Obj)) return false; if (TapiFile *Tapi = dyn_cast(&Obj)) @@ -903,6 +923,42 @@ return '?'; } +static char getSymbolNMTypeChar(XCOFFObjectFile &Obj, symbol_iterator I) { + Expected TypeOrErr = I->getType(); + if (!TypeOrErr) { + warn(TypeOrErr.takeError(), Obj.getFileName(), + "for symbol with index " + + Twine(Obj.getSymbolIndex(I->getRawDataRefImpl().p))); + return '?'; + } + + uint32_t SymType = *TypeOrErr; + + if (SymType == SymbolRef::ST_File) + return 'f'; + + // If the I->getSection() call would return an error, the earlier I->getType() + // call will already have returned the same error first. + section_iterator SecIter = cantFail(I->getSection()); + + if (SecIter == Obj.section_end()) + return '?'; + + if (Obj.isDebugSection(SecIter->getRawDataRefImpl())) + return 'N'; + + if (SecIter->isText()) + return 't'; + + if (SecIter->isData()) + return 'd'; + + if (SecIter->isBSS()) + return 'b'; + + return '?'; +} + static char getSymbolNMTypeChar(COFFImportFile &Obj) { switch (Obj.getCOFFImportHeader()->getType()) { case COFF::IMPORT_CODE: @@ -1051,6 +1107,8 @@ Ret = getSymbolNMTypeChar(*IR, I); else if (COFFObjectFile *COFF = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*COFF, I); + else if (XCOFFObjectFile *XCOFF = dyn_cast(&Obj)) + Ret = getSymbolNMTypeChar(*XCOFF, I); else if (COFFImportFile *COFFImport = dyn_cast(&Obj)) Ret = getSymbolNMTypeChar(*COFFImport); else if (MachOObjectFile *MachO = dyn_cast(&Obj))