Suppose you need to add a section to an ELF binary to contain information gathered at compile time, but to be used at link time or run time. Here's how to add a section named .okdata
and either populate it with data either after or before emitting the ELF binary.
In this case, you'll add file’s contents to an already existing binary. objcopy
copies and translates object files, such that adding a new section is a matter of writing that new section’s contents into the ELF file.
(Optional) Create a simple program’s object file.
$ echo '#include<stdio.h> int main(){puts("Hello world!"); return 0;}' | gcc -c -o hello.o -xc -
Write the section’s contents to a file. You can do this with any hex or text editor, or you can use the shell's coreutils as below. Here, the options -e
avoid a trailing line (otherwise 0x0A
will append at end), and -n
interpretes backslash escapes.
$ echo -en \\x4f\\x4b\\x20\\x43\\x6f\\x6d\\x70\\x75\\x74\\x65\\x72\\x00 > okdata
Add the section file to the object file using objcopy
.
$ objcopy --add-section .okdata=okdata \
--set-section-flags .okdata=noload,readonly hello.o okhello.o
Recompile.
$ clang okhello.o -o hello
Inspect. Here, the -s
option displays the sections' full contents, and -j
displays only the requested section(s).
$ ./hello && objdump -sj .okdata hello
- In llvm/lib/MC/MCObjectFileInfo.cpp, add a section using
llvm::MCContext.getELFSection (const Twine &Section, unsigned Type, unsigned Flags)
.
void MCObjectFileInfo::initELFMCObjectFileInfo(const Triple &T, bool Large) {
/*Snip*/
// ELF
BSSSection = Ctx->getELFSection(".bss", ELF::SHT_NOBITS,
ELF::SHF_WRITE | ELF::SHF_ALLOC);
TextSection = Ctx->getELFSection(".text", ELF::SHT_PROGBITS,
ELF::SHF_EXECINSTR | ELF::SHF_ALLOC);
DataSection = Ctx->getELFSection(".data", ELF::SHT_PROGBITS,
ELF::SHF_WRITE | ELF::SHF_ALLOC);
ReadOnlySection =
Ctx->getELFSection(".rodata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC);
//ADDED for new .okdata section
OKDataSection =
Ctx->getELFSection(".okdata", ELF::SHT_PROGBITS, ELF::SHF_ALLOC);
/*Snip*/
};
The unsigned Type
and unsigned Flags
arguments are formed using bitmasks which are located in llvm/BinaryFormat/ELF.h at line 772 and line 837 respectively. See man -s 5 elf for meanings of the flags.
Note that the order of the calls to Ctx->getELFSection()
reflect the order that the sections appear in the ELF binary.
- In llvm/include/llvm/MC/MCObjectFileInfo.h add a handle to the new section in order for the AsmPrinter to be able to select it for emitting. Add an MCSection declaration and accessor function.
#ifndef LLVM_MC_MCOBJECTFILEINFO_H
#define LLVM_MC_MCOBJECTFILEINFO_H
/* Snip */
class MCObjectFileInfo {
protected:
/*Snip*/
/// Section directive for standard text.
MCSection *TextSection;
/// Section directive for standard data.
MCSection *DataSection;
/// Section that is default initialized to zero.
MCSection *BSSSection;
/// Section that is readonly and can contain arbitrary initialized data.
/// Targets are not required to have a readonly section. If they don't,
/// various bits of code will fall back to using the data section for
/// constants.
MCSection *ReadOnlySection;
/// ADDED: Section for our OKData
MCSection *OKDataSection;
/*Snip*/
public:
/*Snip*/
MCSection *getTextSection() const { return TextSection; }
MCSection *getDataSection() const { return DataSection; }
MCSection *getBSSSection() const { return BSSSection; }
MCSection *getReadOnlySection() const { return ReadOnlySection; }
// ADDED: Accessor returns a pointer to the OKData MCSection.
MCSection *getOKDataSection() const { return OKDataSection; }
/*Snip*/
};
} // end namespace llvm
#endif
llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp contains the code which emits contents of each section.
-
Switch the section to be written to using the
unique_ptr
fieldllvm::AsmPrinter::Outstreamer
, which usesllvm::MCStreamer.SwitchSection()
to point to your added section. -
After switching sections, you can write to it using the MCStreamer class’ emit functions, e.g.
MCStreamer::EmitBytes()
,MCStreamer::EmitZeros()
,MCStreamer::EmitValue()
,MCStreamer::EmitSymbolValue()
.
bool AsmPrinter::doFinalization(Module &M) { /* snip */ OutStreamer->SwitchSection(OutContext.getObjectFileInfo()->getOKDataSection()); const char* okbytes = "\x4f\x4b\x20\x43\x6f\x6d\x70\x75\x74\x65\x72\x00"; OutStreamer->EmitBytes(StringRef(okbytes)); /* snip*/ }
- Write an ImmutablePass performing the analysis. Add an include statement for the pass’ header file.
- Add declaration to void AsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const requiring this pass.
- Use
getAnalysisIfAvailable<GCModuleInfo>()
to get the Immutable Pass’ information. - Write to the section using the techniques discussed above.
I can add the section but it doesn't get loaded, I assume because it's not part of a segment? By the way I'm doing this on a compiled binary