Skip to content

Instantly share code, notes, and snippets.

@colematt
Last active March 1, 2024 20:04
Show Gist options
  • Save colematt/1695ef6498fe150d41494eff780d499c to your computer and use it in GitHub Desktop.
Save colematt/1695ef6498fe150d41494eff780d499c to your computer and use it in GitHub Desktop.
[Add a section to an ELF binary] #llvm

Adding Sections to ELF Binaries

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.

Adding a Section After Emitting

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

Adding a Section Before Emitting

Add the section to the emitted binary

  1. 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.

  1. 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

Writing to this section

llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp contains the code which emits contents of each section.

  1. Switch the section to be written to using the unique_ptr field llvm::AsmPrinter::Outstreamer, which uses llvm::MCStreamer.SwitchSection() to point to your added section.

  2. After switching sections, you can write to it using the MCStreamer class’ emit functions, e.g.

    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*/
    }

Writing the results of a pass’ analysis to the section

  1. Write an ImmutablePass performing the analysis. Add an include statement for the pass’ header file.
  2. Add declaration to void AsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const requiring this pass.
  3. Use getAnalysisIfAvailable<GCModuleInfo>() to get the Immutable Pass’ information.
  4. Write to the section using the techniques discussed above.
//hello.c
#include <stdio.h>
int main() {
puts ("Hello world");
return 0;
}
all: hello
hello.o: hello.c
$(CC) -c $^
okhello.o: hello.o okdata
objcopy --add-section .okdata=okdata --set-section-flags .okdata=noload,readonly hello.o $@
hello: okhello.o
$(CC) $^ -o $@
.PHONY: clean
clean:
rm -fv hello.o okhello.o hello
rm -fv *~ \#*\# *.swp
OK Computer
@Heath123
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment