Using an MCStreamer Directly to produce an object file?

llvm-dev,

Thanks so much in advance for any help, tips, or advice you may be able
to offer me. I'm going to try to avoid the big-picture description of
the project I'm working on, and only talk about the parts that I have
trouble with / currently need to implement. -- I've been starting by
taking the source code from the "llvm-mc" tool, and working that down
into a smaller form that does the kinds of things I want to do at the MC
"layer".

So, I'd like to be able to use a MCStreamer object directly to make some
output file, and object ".o" would be just fine to start with.

I seem to be able to create an aarch64 target without too much issue:

  llvm::InitializeAllTargetInfos();
  llvm::InitializeAllTargetMCs();
  llvm::InitializeAllAsmParsers();
  llvm::InitializeAllDisassemblers();

  std::string Error;
  std::string TripleName("aarch64-unknown-linux-gnu");
  Triple TheTriple(Triple::normalize(TripleName));
  const Target *TheTarget = TargetRegistry::lookupTarget(TripleName,
Error);
  if (!TheTarget) {
    std::cerr << "llvm_insts_to_binary(): " << Error;
    return 1;
  }

Then, I move on to creating some of the needed ASM / REG info and an
MCContext, etc.:

  std::unique_ptr<MCRegisterInfo>
MRI(TheTarget->createMCRegInfo(TripleName));
  assert(MRI && "Unable to create target register info!");
  std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI,
TripleName));
  assert(MAI && "Unable to create target asm info!");
  std::string MCPU("generic");
  std::string FeaturesStr("");
  MCObjectFileInfo MOFI;
  MCContext Ctx(MAI.get(), MRI.get(), &MOFI);
  MOFI.InitMCObjectFileInfo(TheTriple, llvm::Reloc::Model::PIC_,
llvm::CodeModel::Model::Default, Ctx);
  std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
  std::unique_ptr<MCSubtargetInfo>
STI(TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr));

OK, that seems, OK. Now I want to setup an output buffer:

  std::string ofile("/home/users/avose/hpcgp/hpcgp_rundir/tmp.o");
  std::unique_ptr<tool_output_file> Out = GetOutputStream(ofile);
  if (!Out) {
    return 1;
  }
  std::unique_ptr<buffer_ostream> BOS;
  raw_pwrite_stream *OS = &Out->os();
  if (!Out->os().supportsSeeking()) {
    BOS = make_unique<buffer_ostream>(Out->os());
    OS = BOS.get();
  }

This also seems to do OK. Now I would think I can finally get around to
making the code emitter and the MCStreamer:

  MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx);
  MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName,
MCPU);
  MCStreamer *Str = TheTarget->createMCObjectStreamer(TheTriple, Ctx,
*MAB, *OS, CE, *STI, true, false);

So, like, wow, I would think I should be good to go here! I should be
able to just use functions like:

  Str->EmitInstruction();
  Str->EmitLabel();

These should then result in everything I've tried to emit coming out in
my output object file "tmp.o", you know, as long as I'm careful to be
sure to make a call to keep my output file from being deleted. So, when
I'm done making "emit" calls like the above, I be sure to do the keep
call:

Out->keep();

I'm aware that the calls to EmitInstruction(); and EmitLabel(); need to
have their arguments properly constructed first, and I do try to do that
using the context I had set up before, so, something like:

  const llvm::Twine tname("my_label_name");
  llvm::MCSymbol* mcs = Ctx.getOrCreateSymbol(tname);
  Str->EmitLabel(mcs);

Or even something more complicated like setting up an MCInst and all
it's operands:

  llvm::MCInst *llinst = new MCInst();
  llinst->setOpcode(input_opcode);
  llinst->addOperand(llvm::MCOperand::createReg(input_reg0));
  llinst->addOperand(llvm::MCOperand::createReg(input_reg1));
  const llvm::Twine tname("label_name");
  const llvm::MCSymbol* mcs = Ctx.getOrCreateSymbol(tname);
  const llvm::MCSymbolRefExpr *msre = llvm::MCSymbolRefExpr::create(mcs,
Ctx);
  llinst->addOperand(llvm::MCOperand::createExpr(msre));

However, while I don't get any compiler errors, and everything runs to
completion without printing any errors, my output file "tmp.o" is always
created, but just empty with zero bytes. I must be doing something
silly here.. Does anyone have any ideas about what I'm doing wrong, or
perhaps have example code that directly uses the MCStreamer object to
write out object binary files? Some examples showing how to make this
work would _really_ make my week.

This has been hard to debug, as I don't get any errors, just an empty
output .o file...

I was thinking it may be because of section issues, so I did add some
initial sections, hoping that might help:

  Str->InitSections(false);
  MCSection *textsect = Ctx.getELFSection(".text", ELF::SHT_PROGBITS,
ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
  Str->SwitchSection(textsect);

This did not seem to help at all. I'm running out of ideas. Anyone
know how to use an MCStreamer object directly to make and object file?

Thanks so much for your time,
~Aaron Vose

(Software Engineer)
(Cray Inc.)

Personally I looked at llvm-dsymutil as an example of writing a basic object file with MCStreamer when I created llvm-dwp to do a similar task. Perhaps you could model things off either of those?

Did you call “Finish” on your MCStreamer?

Also, I don’t think I needed to use tool_output_file - not sure where that’s from. there’s simple raw_ostreams that go straight to files.