diff --git a/llvm/include/llvm/BinaryFormat/SFrame.h b/llvm/include/llvm/BinaryFormat/SFrame.h index 095db18b9c254..f7e14f05d0d9d 100644 --- a/llvm/include/llvm/BinaryFormat/SFrame.h +++ b/llvm/include/llvm/BinaryFormat/SFrame.h @@ -31,6 +31,24 @@ LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); constexpr uint16_t Magic = 0xdee2; +constexpr int32_t FRERAOffsetInvalid = 0; + +constexpr int32_t S390xCFAOffsetAdjustment = -160; +constexpr int32_t S390xCFAOffsetAlignmentFactor = 8; +constexpr int32_t v2S390xCFAOffsetEncode(int32_t Offset) { + return (Offset + S390xCFAOffsetAdjustment) / S390xCFAOffsetAlignmentFactor; +} +constexpr int32_t v2S390xCFAOffsetDecode(int32_t Offset) { + return (Offset * S390xCFAOffsetAlignmentFactor) - S390xCFAOffsetAdjustment; +} +constexpr bool v2S390xOffsetIsRegnum(int32_t Offset) { return Offset & 1; } +constexpr int32_t v2S390xOffsetEncodeRegnum(int32_t Regnum) { + return (Regnum << 1) | 1; +} +constexpr int32_t v2S390xOffsetDecodeRegnum(int32_t Offset) { + return Offset >> 1; +} + enum class Version : uint8_t { #define HANDLE_SFRAME_VERSION(CODE, NAME) NAME = CODE, #include "llvm/BinaryFormat/SFrameConstants.def" diff --git a/llvm/include/llvm/BinaryFormat/SFrameConstants.def b/llvm/include/llvm/BinaryFormat/SFrameConstants.def index fddd440e41f32..0cdd2a76bf728 100644 --- a/llvm/include/llvm/BinaryFormat/SFrameConstants.def +++ b/llvm/include/llvm/BinaryFormat/SFrameConstants.def @@ -52,6 +52,7 @@ HANDLE_SFRAME_FLAG(0x04, FDEFuncStartPCRel) HANDLE_SFRAME_ABI(0x01, AArch64EndianBig) HANDLE_SFRAME_ABI(0x02, AArch64EndianLittle) HANDLE_SFRAME_ABI(0x03, AMD64EndianLittle) +HANDLE_SFRAME_ABI(0x04, S390xEndianBig) HANDLE_SFRAME_FRE_TYPE(0x00, Addr1) HANDLE_SFRAME_FRE_TYPE(0x01, Addr2) diff --git a/llvm/include/llvm/Object/SFrameParser.h b/llvm/include/llvm/Object/SFrameParser.h index 3ce5d70142a9f..ac5235efdad37 100644 --- a/llvm/include/llvm/Object/SFrameParser.h +++ b/llvm/include/llvm/Object/SFrameParser.h @@ -59,9 +59,16 @@ template class SFrameParser { iterator_range fres(const sframe::FuncDescEntry &FDE, Error &Err) const; + struct RegisterLocation { + enum ValueKind { Register, StackSlot }; + + enum ValueKind Kind; + int32_t Value; + }; + std::optional getCFAOffset(const FrameRowEntry &FRE) const; - std::optional getRAOffset(const FrameRowEntry &FRE) const; - std::optional getFPOffset(const FrameRowEntry &FRE) const; + std::optional getRAOffset(const FrameRowEntry &FRE) const; + std::optional getFPOffset(const FrameRowEntry &FRE) const; ArrayRef getExtraOffsets(const FrameRowEntry &FRE) const; private: @@ -80,6 +87,8 @@ template class SFrameParser { uint64_t getFREBase() const { return getFDEBase() + Header.NumFDEs * sizeof(sframe::FuncDescEntry); } + + RegisterLocation getRegisterLocation(int32_t Offset) const; }; template class SFrameParser::FallibleFREIterator { diff --git a/llvm/lib/Object/SFrameParser.cpp b/llvm/lib/Object/SFrameParser.cpp index 759b579230d9d..efec2635a7f04 100644 --- a/llvm/lib/Object/SFrameParser.cpp +++ b/llvm/lib/Object/SFrameParser.cpp @@ -196,23 +196,61 @@ static std::optional getOffset(ArrayRef Offsets, size_t Idx) { template std::optional SFrameParser::getCFAOffset(const FrameRowEntry &FRE) const { - return getOffset(FRE.Offsets, 0); + std::optional Offset = getOffset(FRE.Offsets, 0); + switch (Header.ABIArch) { + case sframe::ABI::AArch64EndianBig: + case sframe::ABI::AArch64EndianLittle: + case sframe::ABI::AMD64EndianLittle: + return Offset; + + case sframe::ABI::S390xEndianBig: + if (!Offset) + return std::nullopt; + return sframe::v2S390xCFAOffsetDecode(*Offset); + } + llvm_unreachable("Unhandled ABI!"); } template -std::optional +typename SFrameParser::RegisterLocation +SFrameParser::getRegisterLocation(int32_t Offset) const { + switch (Header.ABIArch) { + case sframe::ABI::S390xEndianBig: + if (sframe::v2S390xOffsetIsRegnum(Offset)) + return RegisterLocation{RegisterLocation::Register, + sframe::v2S390xOffsetDecodeRegnum(Offset)}; + [[fallthrough]]; + case sframe::ABI::AArch64EndianBig: + case sframe::ABI::AArch64EndianLittle: + case sframe::ABI::AMD64EndianLittle: + return RegisterLocation{RegisterLocation::StackSlot, Offset}; + } + llvm_unreachable("Unhandled ABI!"); +} + +template +std::optional::RegisterLocation> SFrameParser::getRAOffset(const FrameRowEntry &FRE) const { - if (usesFixedRAOffset()) - return Header.CFAFixedRAOffset; - return getOffset(FRE.Offsets, 1); + std::optional Offset = + usesFixedRAOffset() ? Header.CFAFixedRAOffset : getOffset(FRE.Offsets, 1); + if (!Offset) + return std::nullopt; + if (Header.ABIArch == sframe::ABI::S390xEndianBig && + *Offset == sframe::FRERAOffsetInvalid) + return std::nullopt; + return getRegisterLocation(*Offset); } template -std::optional +std::optional::RegisterLocation> SFrameParser::getFPOffset(const FrameRowEntry &FRE) const { - if (usesFixedFPOffset()) - return Header.CFAFixedFPOffset; - return getOffset(FRE.Offsets, usesFixedRAOffset() ? 1 : 2); + size_t Idx = usesFixedRAOffset() ? 1 : 2; + std::optional Offset = usesFixedFPOffset() + ? Header.CFAFixedFPOffset + : getOffset(FRE.Offsets, Idx); + if (!Offset) + return std::nullopt; + return getRegisterLocation(*Offset); } template diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-fre.test b/llvm/test/tools/llvm-readobj/ELF/sframe-fre.test index 3f1e7d667f47e..023fdc77cc31b 100644 --- a/llvm/test/tools/llvm-readobj/ELF/sframe-fre.test +++ b/llvm/test/tools/llvm-readobj/ELF/sframe-fre.test @@ -151,7 +151,10 @@ Sections: # CASE1-NEXT: Return Address Signed: No # CASE1-NEXT: Offset Size: B1 (0x0) # CASE1-NEXT: Base Register: FP (0x0) -# CASE1-NEXT: RA Offset: 64 +# CASE1-NEXT: RA Offset { +# CASE1-NEXT: Kind: Stack Slot +# CASE1-NEXT: Value: 64 +# CASE1-NEXT: } # CASE1-NEXT: } # CASE1-NEXT: Frame Row Entry { # CASE1-NEXT: Start Address: 0xDE0080 @@ -159,7 +162,10 @@ Sections: # CASE1-NEXT: Offset Size: B4 (0x2) # CASE1-NEXT: Base Register: SP (0x1) # CASE1-NEXT: CFA Offset: 116 -# CASE1-NEXT: RA Offset: 64 +# CASE1-NEXT: RA Offset { +# CASE1-NEXT: Kind: Stack Slot +# CASE1-NEXT: Value: 64 +# CASE1-NEXT: } # CASE1-NEXT: } # CASE1-NEXT: ] # CASE1: FuncDescEntry [1] { @@ -174,8 +180,14 @@ Sections: # CASE1-NEXT: Offset Size: B2 (0x1) # CASE1-NEXT: Base Register: FP (0x0) # CASE1-NEXT: CFA Offset: 16 -# CASE1-NEXT: RA Offset: 64 -# CASE1-NEXT: FP Offset: 32 +# CASE1-NEXT: RA Offset { +# CASE1-NEXT: Kind: Stack Slot +# CASE1-NEXT: Value: 64 +# CASE1-NEXT: } +# CASE1-NEXT: FP Offset { +# CASE1-NEXT: Kind: Stack Slot +# CASE1-NEXT: Value: 32 +# CASE1-NEXT: } # CASE1-NEXT: } # CASE1-NEXT: Frame Row Entry { # CASE1-NEXT: Start Address: 0x100 @@ -183,8 +195,14 @@ Sections: # CASE1-NEXT: Offset Size: B1 (0x0) # CASE1-NEXT: Base Register: FP (0x0) # CASE1-NEXT: CFA Offset: 16 -# CASE1-NEXT: RA Offset: 64 -# CASE1-NEXT: FP Offset: 32 +# CASE1-NEXT: RA Offset { +# CASE1-NEXT: Kind: Stack Slot +# CASE1-NEXT: Value: 64 +# CASE1-NEXT: } +# CASE1-NEXT: FP Offset { +# CASE1-NEXT: Kind: Stack Slot +# CASE1-NEXT: Value: 32 +# CASE1-NEXT: } # CASE1-NEXT: Extra Offsets: [48, 64] # CASE1-NEXT: } # CASE1-NEXT:{{.*}}: warning: '[[FILE]]': unexpected end of data at offset 0x6e while reading [0x6e, 0x71) @@ -279,7 +297,10 @@ Sections: # CASE2-NEXT: Offset Size: B1 (0x0) # CASE2-NEXT: Base Register: FP (0x0) # CASE2-NEXT: CFA Offset: 16 -# CASE2-NEXT: RA Offset: 32 +# CASE2-NEXT: RA Offset { +# CASE2-NEXT: Kind: Stack Slot +# CASE2-NEXT: Value: 32 +# CASE2-NEXT: } # CASE2-NEXT: } # CASE2-NEXT: Frame Row Entry { # CASE2-NEXT: Start Address: 0xDE001F @@ -287,8 +308,14 @@ Sections: # CASE2-NEXT: Offset Size: B1 (0x0) # CASE2-NEXT: Base Register: FP (0x0) # CASE2-NEXT: CFA Offset: 16 -# CASE2-NEXT: RA Offset: 32 -# CASE2-NEXT: FP Offset: 48 +# CASE2-NEXT: RA Offset { +# CASE2-NEXT: Kind: Stack Slot +# CASE2-NEXT: Value: 32 +# CASE2-NEXT: } +# CASE2-NEXT: FP Offset { +# CASE2-NEXT: Kind: Stack Slot +# CASE2-NEXT: Value: 48 +# CASE2-NEXT: } # CASE2-NEXT: } # CASE2-NEXT: Frame Row Entry { # CASE2-NEXT: Start Address: 0xDE0020 @@ -296,8 +323,14 @@ Sections: # CASE2-NEXT: Offset Size: B1 (0x0) # CASE2-NEXT: Base Register: FP (0x0) # CASE2-NEXT: CFA Offset: 16 -# CASE2-NEXT: RA Offset: 32 -# CASE2-NEXT: FP Offset: 48 +# CASE2-NEXT: RA Offset { +# CASE2-NEXT: Kind: Stack Slot +# CASE2-NEXT: Value: 32 +# CASE2-NEXT: } +# CASE2-NEXT: FP Offset { +# CASE2-NEXT: Kind: Stack Slot +# CASE2-NEXT: Value: 48 +# CASE2-NEXT: } # CASE2-NEXT: Extra Offsets: [64] # CASE2-NEXT: } # CASE2-NEXT:{{.*}}: warning: '[[FILE]]': unsupported FRE offset size 3 at offset 0x58 diff --git a/llvm/test/tools/llvm-readobj/ELF/sframe-s390x.test b/llvm/test/tools/llvm-readobj/ELF/sframe-s390x.test new file mode 100644 index 0000000000000..96fceca2bef14 --- /dev/null +++ b/llvm/test/tools/llvm-readobj/ELF/sframe-s390x.test @@ -0,0 +1,111 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-readobj --sframe=.sframe_eof_address --sframe %t 2>&1 | \ +# RUN: FileCheck %s --strict-whitespace --match-full-lines -DFILE=%t + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2MSB + Type: ET_EXEC +Sections: + - Name: .sframe + Type: SHT_GNU_SFRAME + Flags: [ SHF_ALLOC ] + ContentArray: [ + 0xde, 0xe2, 0x02, 0x05, # Preamble (magic, version, flags) + # Header: + 0x04, 0x00, 0x00, 0x00, # ABI, Fixed FP offset, Fixed RA Offset, AUX header length + 0x00, 0x00, 0x00, 0x01, # Number of FDEs + 0x00, 0x00, 0x00, 0x04, # Number of FREs + 0x00, 0x00, 0x00, 0x00, # FRE length + 0x00, 0x00, 0x00, 0x00, # FDE offset + 0x00, 0x00, 0x00, 0x00, # FRE offset + + # FDE: + 0x00, 0xde, 0x00, 0x00, # Start Address + 0x00, 0x00, 0x00, 0xbe, # Size + 0x00, 0x00, 0x00, 0x00, # Start FRE Offset + 0x00, 0x00, 0x00, 0x05, # Number of FREs + 0x02, 0x00, 0x00, 0x00, # Info, RepSize, Padding2 + + # FRE[0]: Zero offsets + 0x00, 0x00, 0x00, 0x00, # Start Address + 0x00, # Info + + # FRE[1]: One offset + 0x00, 0x00, 0x00, 0x01, # Start Address + 0x02, 0x10, # Info, Offset[0] + + # FRE[2]: Two offsets + 0x00, 0x00, 0x00, 0x02, # Start Address + 0x04, 0x10, 0x21, # Info, Offset[0-1] + + # FRE[3]: Three offsets, second offset empty + 0x00, 0x00, 0x00, 0x03, # Start Address + 0x07, 0x10, 0x00, 0x30, # Info, Offset[0-2] + + # FRE[4]: Four offsets + 0x00, 0x00, 0x00, 0x04, # Start Address + 0x08, # Info + 0x10, 0x20, 0x31, 0x40, # Offset[0-3] + + ] +# CHECK-LABEL:SFrame section '.sframe' { +# CHECK: ABI: S390xEndianBig (0x4) +# CHECK: FuncDescEntry [0] { +# CHECK-NEXT: PC: 0xDE001C +# CHECK: Info { +# CHECK-NEXT: FRE Type: Addr4 (0x2) +# CHECK-NEXT: FDE Type: PCInc (0x0) +# CHECK: FREs [ +# CHECK-NEXT: Frame Row Entry { +# CHECK-NEXT: Start Address: 0xDE001C +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: FP (0x0) +# CHECK-NEXT: } +# CHECK-NEXT: Frame Row Entry { +# CHECK-NEXT: Start Address: 0xDE001D +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: FP (0x0) +# CHECK-NEXT: CFA Offset: 288 +# CHECK-NEXT: } +# CHECK-NEXT: Frame Row Entry { +# CHECK-NEXT: Start Address: 0xDE001E +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: FP (0x0) +# CHECK-NEXT: CFA Offset: 288 +# CHECK-NEXT: RA Offset { +# CHECK-NEXT: Kind: Register +# CHECK-NEXT: Value: 16 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: Frame Row Entry { +# CHECK-NEXT: Start Address: 0xDE001F +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: SP (0x1) +# CHECK-NEXT: CFA Offset: 288 +# CHECK-NEXT: FP Offset { +# CHECK-NEXT: Kind: Stack Slot +# CHECK-NEXT: Value: 48 +# CHECK-NEXT: } +# CHECK-NEXT: } +# CHECK-NEXT: Frame Row Entry { +# CHECK-NEXT: Start Address: 0xDE0020 +# CHECK-NEXT: Return Address Signed: No +# CHECK-NEXT: Offset Size: B1 (0x0) +# CHECK-NEXT: Base Register: FP (0x0) +# CHECK-NEXT: CFA Offset: 288 +# CHECK-NEXT: RA Offset { +# CHECK-NEXT: Kind: Stack Slot +# CHECK-NEXT: Value: 32 +# CHECK-NEXT: } +# CHECK-NEXT: FP Offset { +# CHECK-NEXT: Kind: Register +# CHECK-NEXT: Value: 24 +# CHECK-NEXT: } +# CHECK-NEXT: Extra Offsets: [64] +# CHECK-NEXT: } diff --git a/llvm/tools/llvm-readobj/ELFDumper.cpp b/llvm/tools/llvm-readobj/ELFDumper.cpp index a440bad130f4c..56c7d8c09a904 100644 --- a/llvm/tools/llvm-readobj/ELFDumper.cpp +++ b/llvm/tools/llvm-readobj/ELFDumper.cpp @@ -6522,6 +6522,7 @@ void ELFDumper::printSFrameFDEs( sframe::getAArch64PAuthKeys()); break; case sframe::ABI::AMD64EndianLittle: + case sframe::ABI::S390xEndianBig: // unused break; } @@ -6555,10 +6556,22 @@ void ELFDumper::printSFrameFDEs( sframe::getBaseRegisters()); if (std::optional Off = Parser.getCFAOffset(FRE)) W.printNumber("CFA Offset", *Off); - if (std::optional Off = Parser.getRAOffset(FRE)) - W.printNumber("RA Offset", *Off); - if (std::optional Off = Parser.getFPOffset(FRE)) - W.printNumber("FP Offset", *Off); + + using RegisterLocation = + typename SFrameParser::RegisterLocation; + auto PrintLocation = [&](const char *Name, + std::optional Loc) { + if (!Loc) + return; + DictScope LocScope(W, Name); + W.printString("Kind", Loc->Kind == RegisterLocation::Register + ? "Register" + : "Stack Slot"); + W.printNumber("Value", Loc->Value); + }; + PrintLocation("RA Offset", Parser.getRAOffset(FRE)); + PrintLocation("FP Offset", Parser.getFPOffset(FRE)); + if (ArrayRef Offs = Parser.getExtraOffsets(FRE); !Offs.empty()) W.printList("Extra Offsets", Offs); }