Services and Modeling for Embedded Software Development
Embecosm divider strip
Prev  Next

6.4. ELF Object Writer

The ELF Object Writer class is based on the class MCELFObjectTargetWriter and handles the manipulations of fixups and conversion of fixups to relocs.

[Note]Note

Information about the MCELFObjectTargetWriter class can be found in LLVM's documentation at llvm.org/docs/doxygen/html/classllvm_1_1MCELFObjectTargetWriter.html

The constructor for this class simply sets up the parent class, passing it the OS ABI version and the value used to identify the ELF file as belonging to that architecture.

Known machines can be found in an enum in include/llvm/Support/ELF.h. Should the machine architecture not be known, a new value should be used that does not conflict with any other architecture.

For the OpenRISC 1000  architecture, this value is ELF::EM_OPENRISC and has the value 92. The constructor is therefore as follows.

OR1KELFObjectWriter::OR1KELFObjectWriter(uint8_t OSABI)
  : MCELFObjectTargetWriter(/*Is64Bit*/ false, OSABI, ELF::EM_OPENRISC,
                            /*HasRelocationAddend*/ true) {}
        

The GetRelocType function takes a fixup type, mapping it to a relocation type. As in many cases each fixup represents a single representation, the basic structure of this function is a switch statement and setting a variable (Type) to the relocation for the given fixup.

In addition to the custom fixups, there are also some built-in fixups (for 32bit absolute relocation etc.) that also need mapping (these are called FK_Data_4, etc. and can be found in include/llvm/MC/MCFixup.h.

unsigned OR1KELFObjectWriter::GetRelocType(const MCValue &Target,
                                           const MCFixup &Fixup,
                                           bool IsPCRel,
                                           bool IsRelocWithSymbol,
                                           int64_t Addend) const {
  unsigned Type;
  unsigned Kind = (unsigned)Fixup.getKind();
  switch (Kind) {
    default: llvm_unreachable("Invalid fixup kind!");
    case OR1K::fixup_OR1K_PCREL32:
    case FK_PCRel_4:
      Type = ELF::R_OR1K_32_PCREL;
      break;
    ... other fixups note shown ..
  return Type;
}
        

The object writer is enabled in a similar fashion to the other MC components, a function createarchELFObjectWriter is used to create and return a new object writer. This function can be modified for example to provide support for different object writers depending on word size and endianness. A simple example from OpenRISC 1000  is shown below.

MCObjectWriter *llvm::createOR1KELFObjectWriter(raw_ostream &OS,
                                                uint8_t OSABI) {
  MCELFObjectTargetWriter *MOTW = new OR1KELFObjectWriter(OSABI);
  return createELFObjectWriter(MOTW, OS, /*IsLittleEndian=*/ false);
}
        

This function is then used in the previously defined Assembly Backend to set up the object writer when the backend needs it.

MCObjectWriter *OR1KAsmBackend::createObjectWriter(raw_ostream &OS) const {
  return createOR1KELFObjectWriter(OS,
                                   MCELFObjectTargetWriter::getOSABI(OSType));
}
        

Should the architecture require support for multiple object file types, then the function would be modified so that a different object writer is created depending on the OS requested.

Finally support for streaming out object files is added in the archMCTargetDesc.cpp file, by registering a createarchMCStreamer function with the target registry.

In the below example from OpenRISC 1000 , this function checks for if the requested target format is MACH-O or COFF. Neither of these are supported by this implementation, so an error is raised if this is requested.

static MCStreamer *createOR1KMCStreamer(const Target &T, StringRef TT,
                                    MCContext &Ctx, MCAsmBackend &MAB,
                                    raw_ostream &_OS,
                                    MCCodeEmitter *_Emitter,
                                    bool RelaxAll,
                                    bool NoExecStack) {
  Triple TheTriple(TT);
  if (TheTriple.isOSDarwin()) {
    llvm_unreachable("OR1K does not support Darwin MACH-O format");
  }
  if (TheTriple.isOSWindows()) {
    llvm_unreachable("OR1K does not support Windows COFF format");
  }
  return createELFStreamer(Ctx, MAB, _OS, _Emitter, RelaxAll, NoExecStack);
}
        
  // Register the object streamer
  TargetRegistry::RegisterMCObjectStreamer(TheOR1KTarget,
                                           createOR1KMCStreamer);
        
Embecosm divider strip