Skip to content

[GOFF] Add writing of section symbols #133799

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Jun 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c30968f
[GOFF] Introduce GOFFWriter class
redstar Mar 13, 2025
c896d83
[GOFF] Add writing of section symbols
redstar Mar 24, 2025
4fd06dc
Update llvm/include/llvm/MC/MCGOFFSymbolMapper.h
redstar Apr 2, 2025
17a4e27
Update llvm/include/llvm/MC/MCGOFFSymbolMapper.h
redstar Apr 2, 2025
206a975
Update llvm/lib/MC/GOFFObjectWriter.cpp
redstar Apr 2, 2025
9c0e74b
More updates
redstar Apr 2, 2025
a60a4fc
Remove the class GOFFSymbolMapper
redstar Apr 2, 2025
b7605f8
Update formatting
redstar Apr 2, 2025
a3e042b
More updates
redstar Apr 3, 2025
5843b76
Fix failing tests
redstar Apr 4, 2025
5eb61cb
Rework based on reviewer comment.
redstar Apr 5, 2025
835b3d9
More work on reviewer comments
redstar Apr 7, 2025
22b7612
Emit CSECT/CATTR
redstar Apr 7, 2025
4345699
Fix key for hashmap and introduce union
redstar Apr 8, 2025
bc99709
Use getOrdinal/getIndex as the GOFF symbol id.
redstar Apr 8, 2025
3fdb699
Remove initSections()
redstar Apr 8, 2025
21a7b37
Remove RootSDSection
redstar Apr 8, 2025
37942a1
Fix formatting and assertions
redstar Apr 9, 2025
6dc1524
Update the test case to cover all records in the file.
redstar Apr 9, 2025
db2f9d2
Reuse NameSpace from ED in PR/LD
redstar Apr 11, 2025
523aeb0
Also set FillByteValue.
redstar Apr 11, 2025
36b8e4a
- Amode only for LD
redstar Apr 14, 2025
e874216
Emit XATTR
redstar Apr 14, 2025
9624600
Remove comma and add part name.
redstar Apr 15, 2025
f9184fb
Fix test case due to removed comma
redstar Apr 15, 2025
d59806e
Remove DuplicateSymbolSeverity since it is not required.
redstar Apr 15, 2025
95f5689
Change code as recommended by MaskRay
redstar Apr 15, 2025
18bd059
Move .ll test cases
redstar Apr 15, 2025
b521d09
Add another test case, and remove -o - in the run line
redstar Apr 15, 2025
6761c8c
Remove Executable from the ED element
redstar Apr 23, 2025
d778ee0
Remove unused header file
redstar Apr 24, 2025
87e459c
Remove ReadOnly attribute for PR elements
redstar Apr 29, 2025
7a61400
Make alignment of PR always the same as ED
redstar Apr 29, 2025
06fee4e
Fix test cases
redstar Apr 29, 2025
3c1c4f6
Lift fill byte value to higher layer.
redstar Apr 29, 2025
4ccf3ab
Fix formatting.
redstar Apr 30, 2025
e413983
Update comment.
redstar Jun 25, 2025
0eec88f
Remove constants.
redstar Jun 25, 2025
c2d0449
Rebase and fix compile errors due to refactoring.
redstar Jun 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions llvm/include/llvm/BinaryFormat/GOFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,91 @@ enum SubsectionKind : uint8_t {
SK_PPA1 = 2,
SK_PPA2 = 4,
};

// The standard System/390 convention is to name the high-order (leftmost) bit
// in a byte as bit zero. The Flags type helps to set bits in byte according
// to this numeration order.
class Flags {
uint8_t Val = 0;

constexpr static uint8_t bits(uint8_t BitIndex, uint8_t Length, uint8_t Value,
uint8_t OldValue) {
uint8_t Pos = 8 - BitIndex - Length;
uint8_t Mask = ((1 << Length) - 1) << Pos;
Value = Value << Pos;
return (OldValue & ~Mask) | Value;
}

public:
constexpr Flags() = default;
constexpr Flags(uint8_t BitIndex, uint8_t Length, uint8_t Value)
: Val(bits(BitIndex, Length, Value, 0)) {}

template <typename T>
constexpr void set(uint8_t BitIndex, uint8_t Length, T NewValue) {
Val = bits(BitIndex, Length, static_cast<uint8_t>(NewValue), Val);
}

template <typename T>
constexpr T get(uint8_t BitIndex, uint8_t Length) const {
return static_cast<T>((Val >> (8 - BitIndex - Length)) &
((1 << Length) - 1));
}

constexpr operator uint8_t() const { return Val; }
};

// Structure for the flag field of a symbol. See
// https://www.ibm.com/docs/en/zos/3.1.0?topic=formats-external-symbol-definition-record
// at offset 41 for the definition.
struct SymbolFlags {
Flags SymFlags;

#define GOFF_SYMBOL_FLAG(NAME, TYPE, BITINDEX, LENGTH) \
void set##NAME(TYPE Val) { SymFlags.set<TYPE>(BITINDEX, LENGTH, Val); } \
TYPE get##NAME() const { return SymFlags.get<TYPE>(BITINDEX, LENGTH); }

GOFF_SYMBOL_FLAG(FillBytePresence, bool, 0, 1)
GOFF_SYMBOL_FLAG(Mangled, bool, 1, 1)
GOFF_SYMBOL_FLAG(Renameable, bool, 2, 1)
GOFF_SYMBOL_FLAG(RemovableClass, bool, 3, 1)
GOFF_SYMBOL_FLAG(ReservedQwords, ESDReserveQwords, 5, 3)

#undef GOFF_SYMBOL_FLAG

constexpr operator uint8_t() const { return static_cast<uint8_t>(SymFlags); }
};

// Structure for the behavioral attributes. See
// https://www.ibm.com/docs/en/zos/3.1.0?topic=record-external-symbol-definition-behavioral-attributes
// for the definition.
struct BehavioralAttributes {
Flags Attr[10];

#define GOFF_BEHAVIORAL_ATTRIBUTE(NAME, TYPE, ATTRIDX, BITINDEX, LENGTH) \
void set##NAME(TYPE Val) { Attr[ATTRIDX].set<TYPE>(BITINDEX, LENGTH, Val); } \
TYPE get##NAME() const { return Attr[ATTRIDX].get<TYPE>(BITINDEX, LENGTH); }

GOFF_BEHAVIORAL_ATTRIBUTE(Amode, GOFF::ESDAmode, 0, 0, 8)
GOFF_BEHAVIORAL_ATTRIBUTE(Rmode, GOFF::ESDRmode, 1, 0, 8)
GOFF_BEHAVIORAL_ATTRIBUTE(TextStyle, GOFF::ESDTextStyle, 2, 0, 4)
GOFF_BEHAVIORAL_ATTRIBUTE(BindingAlgorithm, GOFF::ESDBindingAlgorithm, 2, 4,
4)
GOFF_BEHAVIORAL_ATTRIBUTE(TaskingBehavior, GOFF::ESDTaskingBehavior, 3, 0, 3)
GOFF_BEHAVIORAL_ATTRIBUTE(ReadOnly, bool, 3, 4, 1)
GOFF_BEHAVIORAL_ATTRIBUTE(Executable, GOFF::ESDExecutable, 3, 5, 3)
GOFF_BEHAVIORAL_ATTRIBUTE(DuplicateSymbolSeverity,
GOFF::ESDDuplicateSymbolSeverity, 4, 2, 2)
GOFF_BEHAVIORAL_ATTRIBUTE(BindingStrength, GOFF::ESDBindingStrength, 4, 4, 4)
GOFF_BEHAVIORAL_ATTRIBUTE(LoadingBehavior, GOFF::ESDLoadingBehavior, 5, 0, 2)
GOFF_BEHAVIORAL_ATTRIBUTE(COMMON, bool, 5, 2, 1)
GOFF_BEHAVIORAL_ATTRIBUTE(IndirectReference, bool, 5, 3, 1)
GOFF_BEHAVIORAL_ATTRIBUTE(BindingScope, GOFF::ESDBindingScope, 5, 4, 4)
GOFF_BEHAVIORAL_ATTRIBUTE(LinkageType, GOFF::ESDLinkageType, 6, 2, 1)
GOFF_BEHAVIORAL_ATTRIBUTE(Alignment, GOFF::ESDAlignment, 6, 3, 5)

#undef GOFF_BEHAVIORAL_ATTRIBUTE
};
} // end namespace GOFF

} // end namespace llvm
Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/TargetLoweringObjectFileImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,10 +316,15 @@ class TargetLoweringObjectFileXCOFF : public TargetLoweringObjectFile {
};

class TargetLoweringObjectFileGOFF : public TargetLoweringObjectFile {
std::string DefaultRootSDName;
std::string DefaultADAPRName;

public:
TargetLoweringObjectFileGOFF();
~TargetLoweringObjectFileGOFF() override = default;

void getModuleMetadata(Module &M) override;

MCSection *SelectSectionForGlobal(const GlobalObject *GO, SectionKind Kind,
const TargetMachine &TM) const override;
MCSection *getExplicitSectionGlobal(const GlobalObject *GO, SectionKind Kind,
Expand Down
16 changes: 12 additions & 4 deletions llvm/include/llvm/MC/MCContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
#include "llvm/BinaryFormat/XCOFF.h"
#include "llvm/MC/MCAsmMacro.h"
#include "llvm/MC/MCDwarf.h"
#include "llvm/MC/MCGOFFAttributes.h"
#include "llvm/MC/MCPseudoProbe.h"
#include "llvm/MC/MCSection.h"
#include "llvm/MC/MCSectionGOFF.h"
#include "llvm/MC/MCSymbolTableEntry.h"
#include "llvm/MC/SectionKind.h"
#include "llvm/Support/Allocator.h"
Expand Down Expand Up @@ -54,7 +56,6 @@ class MCSection;
class MCSectionCOFF;
class MCSectionDXContainer;
class MCSectionELF;
class MCSectionGOFF;
class MCSectionMachO;
class MCSectionSPIRV;
class MCSectionWasm;
Expand Down Expand Up @@ -356,6 +357,10 @@ class MCContext {
MCSymbolXCOFF *createXCOFFSymbolImpl(const MCSymbolTableEntry *Name,
bool IsTemporary);

template <typename TAttr>
MCSectionGOFF *getGOFFSection(SectionKind Kind, StringRef Name,
TAttr SDAttributes, MCSection *Parent);

/// Map of currently defined macros.
StringMap<MCAsmMacro> MacroMap;

Expand Down Expand Up @@ -606,9 +611,12 @@ class MCContext {
getELFUniqueIDForEntsize(StringRef SectionName, unsigned Flags,
unsigned EntrySize);

LLVM_ABI MCSectionGOFF *getGOFFSection(StringRef Section, SectionKind Kind,
MCSection *Parent,
uint32_t Subsection = 0);
MCSectionGOFF *getGOFFSection(SectionKind Kind, StringRef Name,
GOFF::SDAttr SDAttributes);
MCSectionGOFF *getGOFFSection(SectionKind Kind, StringRef Name,
GOFF::EDAttr EDAttributes, MCSection *Parent);
MCSectionGOFF *getGOFFSection(SectionKind Kind, StringRef Name,
GOFF::PRAttr PRAttributes, MCSection *Parent);

LLVM_ABI MCSectionCOFF *
getCOFFSection(StringRef Section, unsigned Characteristics,
Expand Down
92 changes: 92 additions & 0 deletions llvm/include/llvm/MC/MCGOFFAttributes.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//===- MCGOFFAttributes.h - Attributes of GOFF symbols --------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Defines the various attribute collections defining GOFF symbols.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_MC_MCGOFFATTRIBUTES_H
#define LLVM_MC_MCGOFFATTRIBUTES_H

#include "llvm/ADT/StringRef.h"
#include "llvm/BinaryFormat/GOFF.h"
#include <cstdint>

namespace llvm {
namespace GOFF {
// An "External Symbol Definition" in the GOFF file has a type, and depending on
// the type a different subset of the fields is used.
//
// Unlike other formats, a 2 dimensional structure is used to define the
// location of data. For example, the equivalent of the ELF .text section is
// made up of a Section Definition (SD) and a class (Element Definition; ED).
// The name of the SD symbol depends on the application, while the class has the
// predefined name C_CODE/C_CODE64 in AMODE31 and AMODE64 respectively.
//
// Data can be placed into this structure in 2 ways. First, the data (in a text
// record) can be associated with an ED symbol. To refer to data, a Label
// Definition (LD) is used to give an offset into the data a name. When binding,
// the whole data is pulled into the resulting executable, and the addresses
// given by the LD symbols are resolved.
//
// The alternative is to use a Part Definition (PR). In this case, the data (in
// a text record) is associated with the part. When binding, only the data of
// referenced PRs is pulled into the resulting binary.
//
// Both approaches are used. SD, ED, and PR elements are modelled by nested
// MCSectionGOFF instances, while LD elements are associated with MCSymbolGOFF
// instances.

// Attributes for SD symbols.
struct SDAttr {
GOFF::ESDTaskingBehavior TaskingBehavior = GOFF::ESD_TA_Unspecified;
GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
};

// Attributes for ED symbols.
struct EDAttr {
bool IsReadOnly = false;
GOFF::ESDRmode Rmode;
GOFF::ESDNameSpaceId NameSpace = GOFF::ESD_NS_NormalName;
GOFF::ESDTextStyle TextStyle = GOFF::ESD_TS_ByteOriented;
GOFF::ESDBindingAlgorithm BindAlgorithm = GOFF::ESD_BA_Concatenate;
GOFF::ESDLoadingBehavior LoadBehavior = GOFF::ESD_LB_Initial;
GOFF::ESDReserveQwords ReservedQwords = GOFF::ESD_RQ_0;
GOFF::ESDAlignment Alignment = GOFF::ESD_ALIGN_Doubleword;
uint8_t FillByteValue = 0;
};

// Attributes for LD symbols.
struct LDAttr {
bool IsRenamable = false;
GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
GOFF::ESDBindingStrength BindingStrength = GOFF::ESD_BST_Strong;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For LD (as opposed to ER), it seems "strong" is the only allowed value here. Again, if this is true, it doesn't make much sense to specify it.

Copy link
Member Author

@redstar redstar Apr 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually needed for C++ inline functions etc. which can end up in several object files. Also for weak definitions, e.g.:

// a.c
#include <stdio.h>

__attribute__((weak)) void fun() {
  printf("Weak fun\n");
}

void feature() {
  fun();
}

and

// b.c
#include <stdio.h>

extern void feature();

void fun() {
  printf("Other fun\n");
}

int main(int argc, char *argv[]) {
  feature();
  return 0;
}

(example taken from a blog by @MaskRay)

Another case were the documentation needs an update.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. This is not currently reflected in the HLASM output, however. How would one do this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to ask for this. I see only WXTRN for weak externals in the HLASM documentation.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got the hint that WXTRN may work for definitions, too. However, I still need to verify this. In any case, this needs to be handled when the function label is emitted.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like above, right now there doesn't appear to be any code to emit LD symbols to asm output.

GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
GOFF::ESDAmode Amode;
GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
};

// Attributes for PR symbols.
struct PRAttr {
bool IsRenamable = false;
GOFF::ESDExecutable Executable = GOFF::ESD_EXE_Unspecified;
GOFF::ESDLinkageType Linkage = GOFF::ESD_LT_XPLink;
GOFF::ESDBindingScope BindingScope = GOFF::ESD_BSC_Unspecified;
uint32_t SortKey = 0;
};

// Predefined GOFF class names.
constexpr StringLiteral CLASS_CODE = "C_CODE64";
constexpr StringLiteral CLASS_WSA = "C_WSA64";
constexpr StringLiteral CLASS_DATA = "C_DATA64";
constexpr StringLiteral CLASS_PPA2 = "C_@@QPPA2";

} // namespace GOFF
} // namespace llvm

#endif
3 changes: 3 additions & 0 deletions llvm/include/llvm/MC/MCGOFFStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace llvm {
class GOFFObjectWriter;

class MCGOFFStreamer : public MCObjectStreamer {

public:
MCGOFFStreamer(MCContext &Context, std::unique_ptr<MCAsmBackend> MAB,
std::unique_ptr<MCObjectWriter> OW,
Expand All @@ -25,6 +26,8 @@ class MCGOFFStreamer : public MCObjectStreamer {

~MCGOFFStreamer() override;

void changeSection(MCSection *Section, uint32_t Subsection = 0) override;

GOFFObjectWriter &getWriter();

bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
Expand Down
4 changes: 0 additions & 4 deletions llvm/include/llvm/MC/MCObjectFileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ class LLVM_ABI MCObjectFileInfo {
MCSection *GLJMPSection = nullptr;

// GOFF specific sections.
MCSection *PPA1Section = nullptr;
MCSection *PPA2Section = nullptr;
MCSection *PPA2ListSection = nullptr;
MCSection *ADASection = nullptr;
MCSection *IDRLSection = nullptr;
Expand Down Expand Up @@ -439,8 +437,6 @@ class LLVM_ABI MCObjectFileInfo {
MCSection *getGLJMPSection() const { return GLJMPSection; }

// GOFF specific sections.
MCSection *getPPA1Section() const { return PPA1Section; }
MCSection *getPPA2Section() const { return PPA2Section; }
MCSection *getPPA2ListSection() const { return PPA2ListSection; }
MCSection *getADASection() const { return ADASection; }
MCSection *getIDRLSection() const { return IDRLSection; }
Expand Down
81 changes: 71 additions & 10 deletions llvm/include/llvm/MC/MCSectionGOFF.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,95 @@
#define LLVM_MC_MCSECTIONGOFF_H

#include "llvm/BinaryFormat/GOFF.h"
#include "llvm/MC/MCGOFFAttributes.h"
#include "llvm/MC/MCSection.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"

namespace llvm {

class MCExpr;

class MCSectionGOFF final : public MCSection {
private:
MCSection *Parent;
uint32_t Subsection;
// Parent of this section. Implies that the parent is emitted first.
MCSectionGOFF *Parent;

// The attributes of the GOFF symbols.
union {
GOFF::SDAttr SDAttributes;
GOFF::EDAttr EDAttributes;
GOFF::PRAttr PRAttributes;
};

// The type of this section.
GOFF::ESDSymbolType SymbolType;

// Indicates that the PR symbol needs to set the length of the section to a
// non-zero value. This is only a problem with the ADA PR - the binder will
// generate an error in this case.
unsigned RequiresNonZeroLength : 1;

// Set to true if the section definition was already emitted.
mutable unsigned Emitted : 1;

friend class MCContext;
MCSectionGOFF(StringRef Name, SectionKind K, MCSection *P, uint32_t Sub)
friend class MCSymbolGOFF;

MCSectionGOFF(StringRef Name, SectionKind K, GOFF::SDAttr SDAttributes,
MCSectionGOFF *Parent)
: MCSection(SV_GOFF, Name, K.isText(), /*IsVirtual=*/false, nullptr),
Parent(Parent), SDAttributes(SDAttributes),
SymbolType(GOFF::ESD_ST_SectionDefinition), RequiresNonZeroLength(0),
Emitted(0) {}

MCSectionGOFF(StringRef Name, SectionKind K, GOFF::EDAttr EDAttributes,
MCSectionGOFF *Parent)
: MCSection(SV_GOFF, Name, K.isText(), /*IsVirtual=*/false, nullptr),
Parent(Parent), EDAttributes(EDAttributes),
SymbolType(GOFF::ESD_ST_ElementDefinition), RequiresNonZeroLength(0),
Emitted(0) {}

MCSectionGOFF(StringRef Name, SectionKind K, GOFF::PRAttr PRAttributes,
MCSectionGOFF *Parent)
: MCSection(SV_GOFF, Name, K.isText(), /*IsVirtual=*/false, nullptr),
Parent(P), Subsection(Sub) {}
Parent(Parent), PRAttributes(PRAttributes),
SymbolType(GOFF::ESD_ST_PartReference), RequiresNonZeroLength(0),
Emitted(0) {}

public:
void printSwitchToSection(const MCAsmInfo &MAI, const Triple &T,
raw_ostream &OS,
uint32_t /*Subsection*/) const override {
OS << "\t.section\t\"" << getName() << "\"\n";
}
uint32_t Subsection) const override;

bool useCodeAlign() const override { return false; }

MCSection *getParent() const { return Parent; }
uint32_t getSubsection() const { return Subsection; }
// Return the parent section.
MCSectionGOFF *getParent() const { return Parent; }

// Returns the type of this section.
GOFF::ESDSymbolType getSymbolType() const { return SymbolType; }

bool isSD() const { return SymbolType == GOFF::ESD_ST_SectionDefinition; }
bool isED() const { return SymbolType == GOFF::ESD_ST_ElementDefinition; }
bool isPR() const { return SymbolType == GOFF::ESD_ST_PartReference; }

// Accessors to the attributes.
GOFF::SDAttr getSDAttributes() const {
assert(isSD() && "Not a SD section");
return SDAttributes;
}
GOFF::EDAttr getEDAttributes() const {
assert(isED() && "Not a ED section");
return EDAttributes;
}
GOFF::PRAttr getPRAttributes() const {
assert(isPR() && "Not a PR section");
return PRAttributes;
}

bool requiresNonZeroLength() const { return RequiresNonZeroLength; }

void setName(StringRef SectionName) { Name = SectionName; }

static bool classof(const MCSection *S) { return S->getVariant() == SV_GOFF; }
};
Expand Down
Loading