Subzero: Add basic ELFObjectWriter (text section, symtab, strtab, headers).
Able to write out the ELF file header w/ a text section,
a symbol table, and string table. Write text buffer
directly to file after translating each CFG.
This means that the header is written out early w/ fake
data and then we seek back and write the real header
at the very end.
Does not yet handle relocations, data, rodata, constant
pools, bss, or -ffunction-sections, more than 64K sections
or more than 2^24 symbols.
Numbers w/ current NOASSERT=1 build on 176.gcc:
w/out -elf-writer:
0.233771 (21.1%): [ 1287] emit
28MB .s file
w/ -elf-writer:
0.051056 ( 5.6%): [ 1287] emit
2.4MB .o file
BUG=none
R=stichnot@chromium.org
Review URL: https://codereview.chromium.org/678533005
diff --git a/Makefile.standalone b/Makefile.standalone
index d6a2daf..787e138 100644
--- a/Makefile.standalone
+++ b/Makefile.standalone
@@ -92,13 +92,14 @@
assembler_ia32.cpp \
IceCfg.cpp \
IceCfgNode.cpp \
+ IceELFObjectWriter.cpp \
+ IceELFSection.cpp \
IceGlobalContext.cpp \
IceGlobalInits.cpp \
IceInst.cpp \
IceInstX8632.cpp \
IceIntrinsics.cpp \
IceLiveness.cpp \
- IceMemoryRegion.cpp \
IceOperand.cpp \
IceRegAlloc.cpp \
IceRNG.cpp \
@@ -117,6 +118,12 @@
OBJS=$(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS))
+UNITTEST_SRCS = \
+ IceELFSectionTest.cpp
+
+UNITTEST_OBJS = $(patsubst %.cpp, $(OBJDIR)/unittest/%.o, $(UNITTEST_SRCS))
+UNITTEST_LIB_OBJS = $(filter-out $(OBJDIR)/llvm2ice.o,$(OBJS))
+
# Keep all the first target so it's the default.
all: $(OBJDIR)/llvm2ice make_symlink
@@ -141,20 +148,39 @@
$(OBJS): $(OBJDIR)/%.o: src/%.cpp src/*.h src/*.def
$(CXX) -c $(CXXFLAGS) $< -o $@
+$(OBJDIR)/run_unittests: $(UNITTEST_OBJS) $(UNITTEST_LIB_OBJS)
+ $(CXX) $(LDFLAGS) -o $@ $^ $(LLVM_LDFLAGS) -lgtest -lgtest_main -ldl \
+ -Wl,-rpath=$(abspath $(LIBCXX_INSTALL_PATH)/lib)
+
+$(UNITTEST_OBJS): $(OBJDIR)/unittest/%.o: unittest/%.cpp
+ $(CXX) -c $(CXXFLAGS) \
+ -Isrc/ \
+ -I$(LLVM_SRC_PATH)/utils/unittest/googletest/include \
+ -DGTEST_HAS_RTTI=0 -DGTEST_USE_OWN_TR1_TUPLE \
+ $< -o $@
+
$(OBJS): | $(OBJDIR)
+$(UNITTEST_OBJS): | $(OBJDIR)/unittest
+
$(OBJDIR):
@mkdir -p $@
+$(OBJDIR)/unittest: $(OBJDIR)
+ @mkdir -p $@
+
check-lit: llvm2ice make_symlink
LLVM_BIN_PATH=$(LLVM_BIN_PATH) \
$(LLVM_SRC_PATH)/utils/lit/lit.py -sv tests_lit
+check-unit: $(OBJDIR)/run_unittests
+ $(OBJDIR)/run_unittests
+
ifdef MINIMAL
-check: check-lit
+check: check-lit check-unit
@echo "Crosstests ignored, minimal build"
else
-check: check-lit
+check: check-lit check-unit
(cd crosstest; ./runtests.sh)
endif
diff --git a/src/IceCfg.cpp b/src/IceCfg.cpp
index 950a3f8..e40bd16 100644
--- a/src/IceCfg.cpp
+++ b/src/IceCfg.cpp
@@ -405,8 +405,9 @@
Str << "\t.globl\t" << MangledName << "\n";
Str << "\t.type\t" << MangledName << ",@function\n";
}
- Str << "\t.p2align " << getTarget()->getBundleAlignLog2Bytes() << ",0x";
- for (AsmCodeByte I : getTarget()->getNonExecBundlePadding())
+ Assembler *Asm = getAssembler<Assembler>();
+ Str << "\t.p2align " << Asm->getBundleAlignLog2Bytes() << ",0x";
+ for (uint8_t I : Asm->getNonExecBundlePadding())
Str.write_hex(I);
Str << "\n";
Str << MangledName << ":\n";
@@ -444,10 +445,20 @@
TimerMarker T(TimerStack::TT_emit, this);
assert(!Ctx->getFlags().DecorateAsm);
IceString MangledName = getContext()->mangleName(getFunctionName());
- emitTextHeader(MangledName);
+ if (!Ctx->getFlags().UseELFWriter)
+ emitTextHeader(MangledName);
for (CfgNode *Node : Nodes)
Node->emitIAS(this);
- getAssembler<Assembler>()->emitIASBytes(Ctx);
+ // Now write the function to the file and track.
+ if (Ctx->getFlags().UseELFWriter) {
+ getAssembler<Assembler>()->alignFunction();
+ // TODO(jvoung): Transfer remaining fixups too. They may need their
+ // offsets adjusted.
+ Ctx->getObjectWriter()->writeFunctionCode(
+ MangledName, getInternal(), getAssembler<Assembler>()->getBufferView());
+ } else {
+ getAssembler<Assembler>()->emitIASBytes(Ctx);
+ }
}
// Dumps the IR with an optional introductory message.
diff --git a/src/IceClFlags.h b/src/IceClFlags.h
index a1dcf68..68255fb 100644
--- a/src/IceClFlags.h
+++ b/src/IceClFlags.h
@@ -23,17 +23,18 @@
ClFlags()
: DisableInternal(false), SubzeroTimingEnabled(false),
DisableTranslation(false), FunctionSections(false), DataSections(false),
- UseIntegratedAssembler(false), UseSandboxing(false),
- PhiEdgeSplit(false), DecorateAsm(false), DumpStats(false),
- AllowUninitializedGlobals(false), TimeEachFunction(false),
- DisableIRGeneration(false), DefaultGlobalPrefix(""),
- DefaultFunctionPrefix(""), TimingFocusOn(""), VerboseFocusOn(""),
- TranslateOnly("") {}
+ UseELFWriter(false), UseIntegratedAssembler(false),
+ UseSandboxing(false), PhiEdgeSplit(false), DecorateAsm(false),
+ DumpStats(false), AllowUninitializedGlobals(false),
+ TimeEachFunction(false), DisableIRGeneration(false),
+ DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
+ VerboseFocusOn(""), TranslateOnly("") {}
bool DisableInternal;
bool SubzeroTimingEnabled;
bool DisableTranslation;
bool FunctionSections;
bool DataSections;
+ bool UseELFWriter;
bool UseIntegratedAssembler;
bool UseSandboxing;
bool PhiEdgeSplit;
diff --git a/src/IceDefs.h b/src/IceDefs.h
index a28da4b..5d9dd4c 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -33,6 +33,7 @@
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
+#include "llvm/Support/ELF.h"
#include "llvm/Support/raw_ostream.h"
namespace Ice {
@@ -126,6 +127,7 @@
typedef uint32_t VerboseMask;
typedef llvm::raw_ostream Ostream;
+typedef llvm::raw_fd_ostream Fdstream;
} // end of namespace Ice
diff --git a/src/IceELFObjectWriter.cpp b/src/IceELFObjectWriter.cpp
new file mode 100644
index 0000000..f3a63df
--- /dev/null
+++ b/src/IceELFObjectWriter.cpp
@@ -0,0 +1,347 @@
+//===- subzero/src/IceELFObjectWriter.cpp - ELF object file writer --------===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the writer for ELF relocatable object files.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IceDefs.h"
+#include "IceELFObjectWriter.h"
+#include "IceELFSection.h"
+#include "IceELFStreamer.h"
+#include "IceGlobalContext.h"
+#include "IceGlobalInits.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+namespace {
+
+struct {
+ bool IsELF64;
+ uint16_t ELFMachine;
+ uint32_t ELFFlags;
+} ELFTargetInfo[] = {
+#define X(tag, str, is_elf64, e_machine, e_flags) \
+ { is_elf64, e_machine, e_flags } \
+ ,
+ TARGETARCH_TABLE
+#undef X
+};
+
+bool isELF64(TargetArch Arch) {
+ if (Arch < TargetArch_NUM)
+ return ELFTargetInfo[Arch].IsELF64;
+ llvm_unreachable("Invalid target arch for isELF64");
+ return false;
+}
+
+uint16_t getELFMachine(TargetArch Arch) {
+ if (Arch < TargetArch_NUM)
+ return ELFTargetInfo[Arch].ELFMachine;
+ llvm_unreachable("Invalid target arch for getELFMachine");
+ return EM_NONE;
+}
+
+uint32_t getELFFlags(TargetArch Arch) {
+ if (Arch < TargetArch_NUM)
+ return ELFTargetInfo[Arch].ELFFlags;
+ llvm_unreachable("Invalid target arch for getELFFlags");
+ return 0;
+}
+
+} // end of anonymous namespace
+
+ELFObjectWriter::ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out)
+ : Ctx(Ctx), Str(Out), SectionNumbersAssigned(false) {
+ // Create the special bookkeeping sections now.
+ const IceString NullSectionName("");
+ NullSection = new (Ctx.allocate<ELFSection>())
+ ELFSection(NullSectionName, SHT_NULL, 0, 0, 0);
+
+ const IceString ShStrTabName(".shstrtab");
+ ShStrTab = new (Ctx.allocate<ELFStringTableSection>())
+ ELFStringTableSection(ShStrTabName, SHT_STRTAB, 0, 1, 0);
+ ShStrTab->add(ShStrTabName);
+
+ const IceString SymTabName(".symtab");
+ bool IsELF64 = isELF64(Ctx.getTargetArch());
+ const Elf64_Xword SymTabAlign = IsELF64 ? 8 : 4;
+ const Elf64_Xword SymTabEntSize =
+ IsELF64 ? sizeof(Elf64_Sym) : sizeof(Elf32_Sym);
+ static_assert(sizeof(Elf64_Sym) == 24 && sizeof(Elf32_Sym) == 16,
+ "Elf_Sym sizes cannot be derived from sizeof");
+ SymTab = createSection<ELFSymbolTableSection>(SymTabName, SHT_SYMTAB, 0,
+ SymTabAlign, SymTabEntSize);
+ // The first entry in the symbol table should be a NULL entry.
+ const IceString NullSymName("");
+ SymTab->createDefinedSym(NullSymName, STT_NOTYPE, STB_LOCAL, NullSection, 0,
+ 0);
+
+ const IceString StrTabName(".strtab");
+ StrTab =
+ createSection<ELFStringTableSection>(StrTabName, SHT_STRTAB, 0, 1, 0);
+}
+
+template <typename T>
+T *ELFObjectWriter::createSection(const IceString &Name, Elf64_Word ShType,
+ Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
+ Elf64_Xword ShEntsize) {
+ assert(!SectionNumbersAssigned);
+ T *NewSection =
+ new (Ctx.allocate<T>()) T(Name, ShType, ShFlags, ShAddralign, ShEntsize);
+ ShStrTab->add(Name);
+ return NewSection;
+}
+
+template <typename UserSectionList>
+void ELFObjectWriter::assignRelSectionNumInPairs(SizeT &CurSectionNumber,
+ UserSectionList &UserSections,
+ RelSectionList &RelSections,
+ SectionList &AllSections) {
+ RelSectionList::iterator RelIt = RelSections.begin();
+ RelSectionList::iterator RelE = RelSections.end();
+ for (ELFSection *UserSection : UserSections) {
+ UserSection->setNumber(CurSectionNumber++);
+ UserSection->setNameStrIndex(ShStrTab->getIndex(UserSection->getName()));
+ AllSections.push_back(UserSection);
+ if (RelIt != RelE) {
+ ELFRelocationSectionBase *RelSection = *RelIt;
+ if (RelSection->getRelatedSection() == UserSection) {
+ RelSection->setInfoNum(UserSection->getNumber());
+ RelSection->setNumber(CurSectionNumber++);
+ RelSection->setNameStrIndex(ShStrTab->getIndex(RelSection->getName()));
+ AllSections.push_back(RelSection);
+ ++RelIt;
+ }
+ }
+ }
+ // Should finish with UserIt at the same time as RelIt.
+ assert(RelIt == RelE);
+ return;
+}
+
+void ELFObjectWriter::assignRelLinkNum(SizeT SymTabNumber,
+ RelSectionList &RelSections) {
+ for (ELFRelocationSectionBase *S : RelSections) {
+ S->setLinkNum(SymTabNumber);
+ }
+}
+
+void ELFObjectWriter::assignSectionNumbersInfo(SectionList &AllSections) {
+ // Go through each section, assigning them section numbers and
+ // and fill in the size for sections that aren't incrementally updated.
+ assert(!SectionNumbersAssigned);
+ SizeT CurSectionNumber = 0;
+ NullSection->setNumber(CurSectionNumber++);
+ // The rest of the fields are initialized to 0, and stay that way.
+ AllSections.push_back(NullSection);
+
+ assignRelSectionNumInPairs<TextSectionList>(CurSectionNumber, TextSections,
+ RelTextSections, AllSections);
+ assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, DataSections,
+ RelDataSections, AllSections);
+ assignRelSectionNumInPairs<DataSectionList>(CurSectionNumber, RoDataSections,
+ RelRoDataSections, AllSections);
+
+ ShStrTab->setNumber(CurSectionNumber++);
+ ShStrTab->setNameStrIndex(ShStrTab->getIndex(ShStrTab->getName()));
+ AllSections.push_back(ShStrTab);
+
+ SymTab->setNumber(CurSectionNumber++);
+ SymTab->setNameStrIndex(ShStrTab->getIndex(SymTab->getName()));
+ AllSections.push_back(SymTab);
+
+ StrTab->setNumber(CurSectionNumber++);
+ StrTab->setNameStrIndex(ShStrTab->getIndex(StrTab->getName()));
+ AllSections.push_back(StrTab);
+
+ SymTab->setLinkNum(StrTab->getNumber());
+ SymTab->setInfoNum(SymTab->getNumLocals());
+
+ assignRelLinkNum(SymTab->getNumber(), RelTextSections);
+ assignRelLinkNum(SymTab->getNumber(), RelDataSections);
+ assignRelLinkNum(SymTab->getNumber(), RelRoDataSections);
+ SectionNumbersAssigned = true;
+}
+
+Elf64_Off ELFObjectWriter::alignFileOffset(Elf64_Xword Align) {
+ assert(llvm::isPowerOf2_32(Align));
+ Elf64_Off OffsetInFile = Str.tell();
+ Elf64_Xword Mod = OffsetInFile & (Align - 1);
+ if (Mod == 0)
+ return OffsetInFile;
+ Elf64_Xword AlignDiff = Align - Mod;
+ Str.writeZeroPadding(AlignDiff);
+ OffsetInFile += AlignDiff;
+ assert((OffsetInFile & (Align - 1)) == 0);
+ return OffsetInFile;
+}
+
+void ELFObjectWriter::writeFunctionCode(const IceString &FuncName,
+ bool IsInternal,
+ const llvm::StringRef Data) {
+ assert(!SectionNumbersAssigned);
+ // TODO(jvoung): handle ffunction-sections.
+ IceString SectionName = ".text";
+ ELFTextSection *Section = nullptr;
+ if (TextSections.size() == 0) {
+ const Elf64_Xword ShFlags = SHF_ALLOC | SHF_EXECINSTR;
+ // TODO(jvoung): Should be bundle size. Grab it from that target?
+ const Elf64_Xword ShAlign = 32;
+ Section = createSection<ELFTextSection>(SectionName, SHT_PROGBITS, ShFlags,
+ ShAlign, 0);
+ Elf64_Off OffsetInFile = alignFileOffset(Section->getSectionAlign());
+ Section->setFileOffset(OffsetInFile);
+ TextSections.push_back(Section);
+ } else {
+ Section = TextSections[0];
+ }
+ RelocOffsetT OffsetInSection = Section->getCurrentSize();
+ // Function symbols are set to 0 size in the symbol table,
+ // in contrast to data symbols which have a proper size.
+ SizeT SymbolSize = 0;
+ Section->appendData(Str, Data);
+ uint8_t SymbolType;
+ uint8_t SymbolBinding;
+ if (IsInternal) {
+ SymbolType = STT_NOTYPE;
+ SymbolBinding = STB_LOCAL;
+ } else {
+ SymbolType = STT_FUNC;
+ SymbolBinding = STB_GLOBAL;
+ }
+ SymTab->createDefinedSym(FuncName, SymbolType, SymbolBinding, Section,
+ OffsetInSection, SymbolSize);
+ StrTab->add(FuncName);
+}
+
+void ELFObjectWriter::writeDataInitializer(const IceString &VarName,
+ const llvm::StringRef Data) {
+ assert(!SectionNumbersAssigned);
+ (void)Data;
+ llvm_unreachable("TODO");
+ StrTab->add(VarName);
+}
+
+void ELFObjectWriter::writeInitialELFHeader() {
+ assert(!SectionNumbersAssigned);
+ const size_t DummySHOffset = 0;
+ const SizeT DummySHStrIndex = 0;
+ const SizeT DummyNumSections = 0;
+ if (isELF64(Ctx.getTargetArch())) {
+ writeELFHeaderInternal<true>(DummySHOffset, DummySHStrIndex,
+ DummyNumSections);
+ } else {
+ writeELFHeaderInternal<false>(DummySHOffset, DummySHStrIndex,
+ DummyNumSections);
+ }
+}
+
+template <bool IsELF64>
+void ELFObjectWriter::writeELFHeaderInternal(size_t SectionHeaderOffset,
+ SizeT SectHeaderStrIndex,
+ SizeT NumSections) {
+ // Write the e_ident: magic number, class, etc.
+ // The e_ident is byte order and ELF class independent.
+ Str.writeBytes(llvm::StringRef(ElfMagic, strlen(ElfMagic)));
+ Str.write8(IsELF64 ? ELFCLASS64 : ELFCLASS32);
+ Str.write8(ELFDATA2LSB);
+ Str.write8(EV_CURRENT);
+ Str.write8(ELFOSABI_NONE);
+ const uint8_t ELF_ABIVersion = 0;
+ Str.write8(ELF_ABIVersion);
+ Str.writeZeroPadding(EI_NIDENT - EI_PAD);
+
+ // TODO(jvoung): Handle and test > 64K sections. See the generic ABI doc:
+ // https://refspecs.linuxbase.org/elf/gabi4+/ch4.eheader.html
+ // e_shnum should be 0 and then actual number of sections is
+ // stored in the sh_size member of the 0th section.
+ assert(NumSections < SHN_LORESERVE);
+ assert(SectHeaderStrIndex < SHN_LORESERVE);
+
+ // Write the rest of the file header, which does depend on byte order
+ // and ELF class.
+ Str.writeLE16(ET_REL); // e_type
+ Str.writeLE16(getELFMachine(Ctx.getTargetArch())); // e_machine
+ Str.writeELFWord<IsELF64>(1); // e_version
+ // Since this is for a relocatable object, there is no entry point,
+ // and no program headers.
+ Str.writeAddrOrOffset<IsELF64>(0); // e_entry
+ Str.writeAddrOrOffset<IsELF64>(0); // e_phoff
+ Str.writeAddrOrOffset<IsELF64>(SectionHeaderOffset); // e_shoff
+ Str.writeELFWord<IsELF64>(getELFFlags(Ctx.getTargetArch())); // e_flags
+ Str.writeLE16(IsELF64 ? sizeof(Elf64_Ehdr) : sizeof(Elf32_Ehdr)); // e_ehsize
+ static_assert(sizeof(Elf64_Ehdr) == 64 && sizeof(Elf32_Ehdr) == 52,
+ "Elf_Ehdr sizes cannot be derived from sizeof");
+ Str.writeLE16(0); // e_phentsize
+ Str.writeLE16(0); // e_phnum
+ Str.writeLE16(IsELF64 ? sizeof(Elf64_Shdr)
+ : sizeof(Elf32_Shdr)); // e_shentsize
+ static_assert(sizeof(Elf64_Shdr) == 64 && sizeof(Elf32_Shdr) == 40,
+ "Elf_Shdr sizes cannot be derived from sizeof");
+ Str.writeLE16(static_cast<Elf64_Half>(NumSections)); // e_shnum
+ Str.writeLE16(static_cast<Elf64_Half>(SectHeaderStrIndex)); // e_shstrndx
+}
+
+void ELFObjectWriter::writeNonUserSections() {
+ bool IsELF64 = isELF64(Ctx.getTargetArch());
+
+ // Write out the shstrtab now that all sections are known.
+ ShStrTab->doLayout();
+ ShStrTab->setSize(ShStrTab->getSectionDataSize());
+ Elf64_Off ShStrTabOffset = alignFileOffset(ShStrTab->getSectionAlign());
+ ShStrTab->setFileOffset(ShStrTabOffset);
+ Str.writeBytes(ShStrTab->getSectionData());
+
+ SectionList AllSections;
+ assignSectionNumbersInfo(AllSections);
+
+ // Finalize the regular StrTab and fix up references in the SymTab.
+ StrTab->doLayout();
+ StrTab->setSize(StrTab->getSectionDataSize());
+
+ SymTab->updateIndices(StrTab);
+
+ Elf64_Off SymTabOffset = alignFileOffset(SymTab->getSectionAlign());
+ SymTab->setFileOffset(SymTabOffset);
+ SymTab->setSize(SymTab->getSectionDataSize());
+ SymTab->writeData(Str, IsELF64);
+
+ Elf64_Off StrTabOffset = alignFileOffset(StrTab->getSectionAlign());
+ StrTab->setFileOffset(StrTabOffset);
+ Str.writeBytes(StrTab->getSectionData());
+
+ // TODO: Write out the relocation sections.
+ // May also be able to seek around the file and resolve function calls
+ // that are for functions within the same section.
+
+ // Write out the section headers.
+ const size_t ShdrAlign = IsELF64 ? 8 : 4;
+ Elf64_Off ShOffset = alignFileOffset(ShdrAlign);
+ for (const auto S : AllSections) {
+ if (IsELF64)
+ S->writeHeader<true>(Str);
+ else
+ S->writeHeader<false>(Str);
+ }
+
+ // Finally write the updated ELF header w/ the correct number of sections.
+ Str.seek(0);
+ if (IsELF64) {
+ writeELFHeaderInternal<true>(ShOffset, ShStrTab->getNumber(),
+ AllSections.size());
+ } else {
+ writeELFHeaderInternal<false>(ShOffset, ShStrTab->getNumber(),
+ AllSections.size());
+ }
+}
+
+} // end of namespace Ice
diff --git a/src/IceELFObjectWriter.h b/src/IceELFObjectWriter.h
new file mode 100644
index 0000000..1dc9daa
--- /dev/null
+++ b/src/IceELFObjectWriter.h
@@ -0,0 +1,125 @@
+//===- subzero/src/IceELFObjectWriter.h - ELF object writer -----*- C++ -*-===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Abstraction for a writer that is responsible for writing an ELF file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEELFOBJECTWRITER_H
+#define SUBZERO_SRC_ICEELFOBJECTWRITER_H
+
+#include "IceDefs.h"
+#include "IceELFSection.h"
+#include "IceELFStreamer.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+// Higher level ELF object writer. Manages section information and writes
+// the final ELF object. The object writer will write to file the code
+// and data as it is being defined (rather than keep a copy).
+// After all definitions are written out, it will finalize the bookkeeping
+// sections and write them out. Expected usage:
+//
+// (1) writeInitialELFHeader
+// (2) writeDataInitializer*
+// (3) writeFunctionCode*
+// (4) writeNonUserSections
+class ELFObjectWriter {
+ ELFObjectWriter(const ELFObjectWriter &) = delete;
+ ELFObjectWriter &operator=(const ELFObjectWriter &) = delete;
+
+public:
+ ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out);
+
+ // Write the initial ELF header. This is just to reserve space in the ELF
+ // file. Reserving space allows the other functions to write text
+ // and data directly to the file and get the right file offsets.
+ void writeInitialELFHeader();
+
+ // Copy data of a function's text section to file and note the offset of the
+ // symbol's definition in the symbol table.
+ // TODO(jvoung): This also needs the relocations to adjust the
+ // section-relative offsets and hook them up to the symbol table references.
+ void writeFunctionCode(const IceString &FuncName, bool IsInternal,
+ const llvm::StringRef Data);
+
+ // Copy initializer data for a global to file and note the offset and
+ // size of the global's definition in the symbol table.
+ // TODO(jvoung): This needs to know which section. This also needs the
+ // relocations to hook them up to the symbol table references. Also
+ // TODO is handling BSS (which just needs to note the size).
+ void writeDataInitializer(const IceString &VarName,
+ const llvm::StringRef Data);
+
+ // Do final layout and write out the rest of the object file, then
+ // patch up the initial ELF header with the final info.
+ void writeNonUserSections();
+
+private:
+ GlobalContext &Ctx;
+ ELFStreamer &Str;
+ bool SectionNumbersAssigned;
+
+ // All created sections, separated into different pools.
+ typedef std::vector<ELFSection *> SectionList;
+ typedef std::vector<ELFTextSection *> TextSectionList;
+ typedef std::vector<ELFDataSection *> DataSectionList;
+ typedef std::vector<ELFRelocationSectionBase *> RelSectionList;
+ TextSectionList TextSections;
+ RelSectionList RelTextSections;
+ DataSectionList DataSections;
+ RelSectionList RelDataSections;
+ DataSectionList RoDataSections;
+ RelSectionList RelRoDataSections;
+
+ // Handles to special sections that need incremental bookkeeping.
+ ELFSection *NullSection;
+ ELFStringTableSection *ShStrTab;
+ ELFSymbolTableSection *SymTab;
+ ELFStringTableSection *StrTab;
+
+ template <typename T>
+ T *createSection(const IceString &Name, Elf64_Word ShType,
+ Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
+ Elf64_Xword ShEntsize);
+
+ // Align the file position before writing out a section's data,
+ // and return the position of the file.
+ Elf64_Off alignFileOffset(Elf64_Xword Align);
+
+ // Assign an ordering / section numbers to each section.
+ // Fill in other information that is only known near the end
+ // (such as the size, if it wasn't already incrementally updated).
+ // This then collects all sections in the decided order, into one vector,
+ // for conveniently writing out all of the section headers.
+ void assignSectionNumbersInfo(SectionList &AllSections);
+
+ // This function assigns .foo and .rel.foo consecutive section numbers.
+ // It also sets the relocation section's sh_info field to the related
+ // section's number.
+ template <typename UserSectionList>
+ void assignRelSectionNumInPairs(SizeT &CurSectionNumber,
+ UserSectionList &UserSections,
+ RelSectionList &RelSections,
+ SectionList &AllSections);
+
+ // Link the relocation sections to the symbol table.
+ void assignRelLinkNum(SizeT SymTabNumber, RelSectionList &RelSections);
+
+ // Write the ELF file header with the given information about sections.
+ template <bool IsELF64>
+ void writeELFHeaderInternal(uint64_t SectionHeaderOffset,
+ SizeT SectHeaderStrIndex, SizeT NumSections);
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEELFOBJECTWRITER_H
diff --git a/src/IceELFSection.cpp b/src/IceELFSection.cpp
new file mode 100644
index 0000000..1910e77
--- /dev/null
+++ b/src/IceELFSection.cpp
@@ -0,0 +1,159 @@
+//===- subzero/src/IceELFSection.cpp - Representation of ELF sections -----===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines how ELF sections are represented.
+//
+//===----------------------------------------------------------------------===//
+
+#include "IceDefs.h"
+#include "IceELFSection.h"
+#include "IceELFStreamer.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+// Text sections.
+
+void ELFTextSection::appendData(ELFStreamer &Str,
+ const llvm::StringRef MoreData) {
+ Str.writeBytes(MoreData);
+ Header.sh_size += MoreData.size();
+}
+
+// Data sections.
+
+void ELFDataSection::appendData(ELFStreamer &Str,
+ const llvm::StringRef MoreData) {
+ Str.writeBytes(MoreData);
+ Header.sh_size += MoreData.size();
+}
+
+// Relocation sections.
+
+// Symbol tables.
+
+void ELFSymbolTableSection::createDefinedSym(const IceString &Name,
+ uint8_t Type, uint8_t Binding,
+ ELFSection *Section,
+ RelocOffsetT Offset, SizeT Size) {
+ ELFSym NewSymbol = ELFSym();
+ NewSymbol.Sym.setBindingAndType(Binding, Type);
+ NewSymbol.Sym.st_value = Offset;
+ NewSymbol.Sym.st_size = Size;
+ NewSymbol.Number = ELFSym::UnknownNumber;
+ SymtabKey Key = {Name, Section};
+ bool Unique;
+ if (Type == STB_LOCAL)
+ Unique = LocalSymbols.insert(std::make_pair(Key, NewSymbol)).second;
+ else
+ Unique = GlobalSymbols.insert(std::make_pair(Key, NewSymbol)).second;
+ assert(Unique);
+}
+
+void ELFSymbolTableSection::noteUndefinedSym(const IceString &Name,
+ ELFSection *NullSection) {
+ ELFSym NewSymbol = ELFSym();
+ NewSymbol.Sym.setBindingAndType(STB_GLOBAL, STT_NOTYPE);
+ NewSymbol.Number = ELFSym::UnknownNumber;
+ SymtabKey Key = {Name, NullSection};
+ GlobalSymbols.insert(std::make_pair(Key, NewSymbol));
+}
+
+void ELFSymbolTableSection::updateIndices(const ELFStringTableSection *StrTab) {
+ SizeT SymNumber = 0;
+ for (auto &KeyValue : LocalSymbols) {
+ const IceString &Name = KeyValue.first.first;
+ ELFSection *Section = KeyValue.first.second;
+ Elf64_Sym &SymInfo = KeyValue.second.Sym;
+ if (!Name.empty())
+ SymInfo.st_name = StrTab->getIndex(Name);
+ SymInfo.st_shndx = Section->getNumber();
+ KeyValue.second.setNumber(SymNumber++);
+ }
+ for (auto &KeyValue : GlobalSymbols) {
+ const IceString &Name = KeyValue.first.first;
+ ELFSection *Section = KeyValue.first.second;
+ Elf64_Sym &SymInfo = KeyValue.second.Sym;
+ if (!Name.empty())
+ SymInfo.st_name = StrTab->getIndex(Name);
+ SymInfo.st_shndx = Section->getNumber();
+ KeyValue.second.setNumber(SymNumber++);
+ }
+}
+
+void ELFSymbolTableSection::writeData(ELFStreamer &Str, bool IsELF64) {
+ if (IsELF64) {
+ writeSymbolMap<true>(Str, LocalSymbols);
+ writeSymbolMap<true>(Str, GlobalSymbols);
+ } else {
+ writeSymbolMap<false>(Str, LocalSymbols);
+ writeSymbolMap<false>(Str, GlobalSymbols);
+ }
+}
+
+// String tables.
+
+void ELFStringTableSection::add(const IceString &Str) {
+ assert(!isLaidOut());
+ assert(!Str.empty());
+ StringToIndexMap.insert(std::make_pair(Str, UnknownIndex));
+}
+
+size_t ELFStringTableSection::getIndex(const IceString &Str) const {
+ assert(isLaidOut());
+ StringToIndexType::const_iterator It = StringToIndexMap.find(Str);
+ if (It == StringToIndexMap.end()) {
+ llvm_unreachable("String index not found");
+ return UnknownIndex;
+ }
+ return It->second;
+}
+
+bool ELFStringTableSection::SuffixComparator::
+operator()(const IceString &StrA, const IceString &StrB) const {
+ size_t LenA = StrA.size();
+ size_t LenB = StrB.size();
+ size_t CommonLen = std::min(LenA, LenB);
+ // If there is a difference in the common suffix, use that diff to sort.
+ for (size_t i = 0; i < CommonLen; ++i) {
+ char a = StrA[LenA - i - 1];
+ char b = StrB[LenB - i - 1];
+ if (a != b)
+ return a > b;
+ }
+ // If the common suffixes are completely equal, let the longer one come
+ // first, so that it can be laid out first and its characters shared.
+ return LenA > LenB;
+}
+
+void ELFStringTableSection::doLayout() {
+ assert(!isLaidOut());
+ llvm::StringRef Prev;
+
+ // String table starts with 0 byte.
+ StringData.push_back(0);
+
+ for (auto &StringIndex : StringToIndexMap) {
+ assert(StringIndex.second == UnknownIndex);
+ llvm::StringRef Cur = llvm::StringRef(StringIndex.first);
+ if (Prev.endswith(Cur)) {
+ // Prev is already in the StringData, and Cur is shorter than Prev
+ // based on the sort.
+ StringIndex.second = StringData.size() - Cur.size() - 1;
+ continue;
+ }
+ StringIndex.second = StringData.size();
+ std::copy(Cur.begin(), Cur.end(), back_inserter(StringData));
+ StringData.push_back(0);
+ Prev = Cur;
+ }
+}
+
+} // end of namespace Ice
diff --git a/src/IceELFSection.h b/src/IceELFSection.h
new file mode 100644
index 0000000..227affe
--- /dev/null
+++ b/src/IceELFSection.h
@@ -0,0 +1,324 @@
+//===- subzero/src/IceELFSection.h - Model of ELF sections ------*- C++ -*-===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Representation of ELF sections.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEELFSECTION_H
+#define SUBZERO_SRC_ICEELFSECTION_H
+
+#include "IceDefs.h"
+#include "IceELFStreamer.h"
+
+using namespace llvm::ELF;
+
+namespace Ice {
+
+class ELFStreamer;
+class ELFStringTableSection;
+
+// Base representation of an ELF section.
+class ELFSection {
+ ELFSection(const ELFSection &) = delete;
+ ELFSection &operator=(const ELFSection &) = delete;
+
+public:
+ // Sentinel value for a section number/index for before the final
+ // section index is actually known. The dummy NULL section will be assigned
+ // number 0, and it is referenced by the dummy 0-th symbol in the symbol
+ // table, so use max() instead of 0.
+ enum { NoSectionNumber = std::numeric_limits<SizeT>::max() };
+
+ // Constructs an ELF section, filling in fields that will be known
+ // once the *type* of section is decided. Other fields may be updated
+ // incrementally or only after the program is completely defined.
+ ELFSection(const IceString &Name, Elf64_Word ShType, Elf64_Xword ShFlags,
+ Elf64_Xword ShAddralign, Elf64_Xword ShEntsize)
+ : Name(Name), Header(), Number(NoSectionNumber) {
+ Header.sh_type = ShType;
+ Header.sh_flags = ShFlags;
+ Header.sh_addralign = ShAddralign;
+ Header.sh_entsize = ShEntsize;
+ }
+
+ // Set the section number/index after it is finally known.
+ void setNumber(SizeT N) {
+ // Should only set the number once: from NoSectionNumber -> N.
+ assert(Number == NoSectionNumber);
+ Number = N;
+ }
+ SizeT getNumber() const {
+ assert(Number != NoSectionNumber);
+ return Number;
+ }
+
+ void setSize(Elf64_Xword sh_size) { Header.sh_size = sh_size; }
+ SizeT getCurrentSize() const { return Header.sh_size; }
+
+ void setNameStrIndex(Elf64_Word sh_name) { Header.sh_name = sh_name; }
+
+ IceString getName() const { return Name; }
+
+ void setLinkNum(Elf64_Word sh_link) { Header.sh_link = sh_link; }
+
+ void setInfoNum(Elf64_Word sh_info) { Header.sh_info = sh_info; }
+
+ void setFileOffset(Elf64_Off sh_offset) { Header.sh_offset = sh_offset; }
+
+ Elf64_Xword getSectionAlign() const { return Header.sh_addralign; }
+
+ // Write the section header out with the given streamer.
+ template <bool IsELF64> void writeHeader(ELFStreamer &Str);
+
+protected:
+ ~ELFSection() {}
+
+ // Name of the section in convenient string form (instead of a index
+ // into the Section Header String Table, which is not known till later).
+ IceString Name;
+
+ // The fields of the header. May only be partially initialized, but should
+ // be fully initialized before writing.
+ Elf64_Shdr Header;
+
+ // The number of the section after laying out sections.
+ SizeT Number;
+};
+
+// Models text/code sections. Code is written out incrementally and the
+// size of the section is then updated incrementally.
+class ELFTextSection : public ELFSection {
+ ELFTextSection(const ELFTextSection &) = delete;
+ ELFTextSection &operator=(const ELFTextSection &) = delete;
+
+public:
+ using ELFSection::ELFSection;
+
+ void appendData(ELFStreamer &Str, const llvm::StringRef MoreData);
+};
+
+// Models data/rodata sections. Data is written out incrementally and the
+// size of the section is then updated incrementally.
+// Some rodata sections may have fixed entsize and duplicates may be mergeable.
+class ELFDataSection : public ELFSection {
+ ELFDataSection(const ELFDataSection &) = delete;
+ ELFDataSection &operator=(const ELFDataSection &) = delete;
+
+public:
+ using ELFSection::ELFSection;
+
+ void appendData(ELFStreamer &Str, const llvm::StringRef MoreData);
+};
+
+// Model of ELF symbol table entries. Besides keeping track of the fields
+// required for an elf symbol table entry it also tracks the number that
+// represents the symbol's final index in the symbol table.
+struct ELFSym {
+ Elf64_Sym Sym;
+ SizeT Number;
+
+ // Sentinel value for symbols that haven't been assigned a number yet.
+ // The dummy 0-th symbol will be assigned number 0, so don't use that.
+ enum { UnknownNumber = std::numeric_limits<SizeT>::max() };
+
+ void setNumber(SizeT N) {
+ assert(Number == UnknownNumber);
+ Number = N;
+ }
+
+ SizeT getNumber() const {
+ assert(Number != UnknownNumber);
+ return Number;
+ }
+};
+
+// Models a symbol table. Symbols may be added up until updateIndices is
+// called. At that point the indices of each symbol will be finalized.
+class ELFSymbolTableSection : public ELFSection {
+public:
+ using ELFSection::ELFSection;
+
+ // Create initial entry for a symbol when it is defined.
+ // Each entry should only be defined once.
+ // We might want to allow Name to be a dummy name initially, then
+ // get updated to the real thing, since Data initializers are read
+ // before the bitcode's symbol table is read.
+ void createDefinedSym(const IceString &Name, uint8_t Type, uint8_t Binding,
+ ELFSection *Section, RelocOffsetT Offset, SizeT Size);
+
+ // Note that a symbol table entry needs to be created for the given
+ // symbol because it is undefined.
+ void noteUndefinedSym(const IceString &Name, ELFSection *NullSection);
+
+ size_t getSectionDataSize() const {
+ return (LocalSymbols.size() + GlobalSymbols.size()) * Header.sh_entsize;
+ }
+
+ size_t getNumLocals() const { return LocalSymbols.size(); }
+
+ void updateIndices(const ELFStringTableSection *StrTab);
+
+ void writeData(ELFStreamer &Str, bool IsELF64);
+
+private:
+ // Map from symbol name + section to its symbol information.
+ typedef std::pair<IceString, ELFSection *> SymtabKey;
+ typedef std::map<SymtabKey, ELFSym> SymMap;
+
+ template <bool IsELF64>
+ void writeSymbolMap(ELFStreamer &Str, const SymMap &Map);
+
+ // Keep Local and Global symbols separate, since the sh_info needs to
+ // know the index of the last LOCAL.
+ SymMap LocalSymbols;
+ SymMap GlobalSymbols;
+};
+
+// Base model of a relocation section.
+class ELFRelocationSectionBase : public ELFSection {
+ ELFRelocationSectionBase(const ELFRelocationSectionBase &) = delete;
+ ELFRelocationSectionBase &
+ operator=(const ELFRelocationSectionBase &) = delete;
+
+public:
+ ELFRelocationSectionBase(const IceString &Name, Elf64_Word ShType,
+ Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
+ Elf64_Xword ShEntsize, ELFSection *RelatedSection)
+ : ELFSection(Name, ShType, ShFlags, ShAddralign, ShEntsize),
+ RelatedSection(RelatedSection) {}
+
+ ELFSection *getRelatedSection() const { return RelatedSection; }
+
+private:
+ ELFSection *RelatedSection;
+};
+
+// ELFRelocationSection which depends on the actual relocation type.
+// Specializations are needed depending on the ELFCLASS and whether
+// or not addends are explicit or implicitly embedded in the related
+// section (ELFCLASS64 pack their r_info field differently from ELFCLASS32).
+template <typename RelType>
+class ELFRelocationSection : ELFRelocationSectionBase {
+ ELFRelocationSection(const ELFRelocationSectionBase &) = delete;
+ ELFRelocationSection &operator=(const ELFRelocationSectionBase &) = delete;
+
+public:
+ using ELFRelocationSectionBase::ELFRelocationSectionBase;
+
+ void addRelocations() {
+ // TODO: fill me in
+ }
+
+private:
+ typedef std::pair<RelType, ELFSym *> ELFRelSym;
+ typedef std::vector<ELFRelSym> RelocationList;
+ RelocationList Relocations;
+};
+
+// Models a string table. The user will build the string table by
+// adding strings incrementally. At some point, all strings should be
+// known and doLayout() should be called. After that, no other
+// strings may be added. However, the final offsets of the strings
+// can be discovered and used to fill out section headers and symbol
+// table entries.
+class ELFStringTableSection : public ELFSection {
+ ELFStringTableSection(const ELFStringTableSection &) = delete;
+ ELFStringTableSection &operator=(const ELFStringTableSection &) = delete;
+
+public:
+ using ELFSection::ELFSection;
+
+ // Add a string to the table, in preparation for final layout.
+ void add(const IceString &Str);
+
+ // Finalizes the layout of the string table and fills in the section Data.
+ void doLayout();
+
+ // The first byte of the string table should be \0, so it is an
+ // invalid index. Indices start out as unknown until layout is complete.
+ enum { UnknownIndex = 0 };
+
+ // Grabs the final index of a string after layout. Returns UnknownIndex
+ // if the string's index is not found.
+ size_t getIndex(const IceString &Str) const;
+
+ llvm::StringRef getSectionData() const {
+ assert(isLaidOut());
+ return llvm::StringRef(reinterpret_cast<const char *>(StringData.data()),
+ StringData.size());
+ }
+
+ size_t getSectionDataSize() const { return getSectionData().size(); }
+
+private:
+ bool isLaidOut() const { return !StringData.empty(); }
+
+ // Strings can share a string table entry if they share the same
+ // suffix. E.g., "pop" and "lollipop" can both use the characters
+ // in "lollipop", but "pops" cannot, and "unpop" cannot either.
+ // Though, "pop", "lollipop", and "unpop" share "pop" as the suffix,
+ // "pop" can only share the characters with one of them.
+ struct SuffixComparator {
+ bool operator()(const IceString &StrA, const IceString &StrB) const;
+ };
+
+ typedef std::map<IceString, size_t, SuffixComparator> StringToIndexType;
+
+ // Track strings to their index. Index will be UnknownIndex if not
+ // yet laid out.
+ StringToIndexType StringToIndexMap;
+
+ typedef std::vector<uint8_t> RawDataType;
+ RawDataType StringData;
+};
+
+template <bool IsELF64> void ELFSection::writeHeader(ELFStreamer &Str) {
+ Str.writeELFWord<IsELF64>(Header.sh_name);
+ Str.writeELFWord<IsELF64>(Header.sh_type);
+ Str.writeELFXword<IsELF64>(Header.sh_flags);
+ Str.writeAddrOrOffset<IsELF64>(Header.sh_addr);
+ Str.writeAddrOrOffset<IsELF64>(Header.sh_offset);
+ Str.writeELFXword<IsELF64>(Header.sh_size);
+ Str.writeELFWord<IsELF64>(Header.sh_link);
+ Str.writeELFWord<IsELF64>(Header.sh_info);
+ Str.writeELFXword<IsELF64>(Header.sh_addralign);
+ Str.writeELFXword<IsELF64>(Header.sh_entsize);
+}
+
+template <bool IsELF64>
+void ELFSymbolTableSection::writeSymbolMap(ELFStreamer &Str,
+ const SymMap &Map) {
+ // The order of the fields is different, so branch on IsELF64.
+ if (IsELF64) {
+ for (auto &KeyValue : Map) {
+ const Elf64_Sym &SymInfo = KeyValue.second.Sym;
+ Str.writeELFWord<IsELF64>(SymInfo.st_name);
+ Str.write8(SymInfo.st_info);
+ Str.write8(SymInfo.st_other);
+ Str.writeLE16(SymInfo.st_shndx);
+ Str.writeAddrOrOffset<IsELF64>(SymInfo.st_value);
+ Str.writeELFXword<IsELF64>(SymInfo.st_size);
+ }
+ } else {
+ for (auto &KeyValue : Map) {
+ const Elf64_Sym &SymInfo = KeyValue.second.Sym;
+ Str.writeELFWord<IsELF64>(SymInfo.st_name);
+ Str.writeAddrOrOffset<IsELF64>(SymInfo.st_value);
+ Str.writeELFWord<IsELF64>(SymInfo.st_size);
+ Str.write8(SymInfo.st_info);
+ Str.write8(SymInfo.st_other);
+ Str.writeLE16(SymInfo.st_shndx);
+ }
+ }
+}
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEELFSECTION_H
diff --git a/src/IceELFStreamer.h b/src/IceELFStreamer.h
new file mode 100644
index 0000000..ce70e86
--- /dev/null
+++ b/src/IceELFStreamer.h
@@ -0,0 +1,84 @@
+//===- subzero/src/IceELFStreamer.h - Low level ELF writing -----*- C++ -*-===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Interface for serializing bits for common ELF types (words, extended words,
+// etc.), based on the ELF Class.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEELFSTREAMER_H
+#define SUBZERO_SRC_ICEELFSTREAMER_H
+
+#include "IceDefs.h"
+
+namespace Ice {
+
+// Low level writer that can that can handle ELFCLASS32/64.
+// Little endian only for now.
+class ELFStreamer {
+public:
+ explicit ELFStreamer(Fdstream &Out) : Out(Out) {}
+
+ void write8(uint8_t Value) { Out << char(Value); }
+
+ void writeLE16(uint16_t Value) {
+ write8(uint8_t(Value));
+ write8(uint8_t(Value >> 8));
+ }
+
+ void writeLE32(uint32_t Value) {
+ writeLE16(uint16_t(Value));
+ writeLE16(uint16_t(Value >> 16));
+ }
+
+ void writeLE64(uint64_t Value) {
+ writeLE32(uint32_t(Value));
+ writeLE32(uint32_t(Value >> 32));
+ }
+
+ template <bool IsELF64, typename T> void writeAddrOrOffset(T Value) {
+ if (IsELF64)
+ writeLE64(Value);
+ else
+ writeLE32(Value);
+ }
+
+ template <bool IsELF64, typename T> void writeELFWord(T Value) {
+ writeLE32(Value);
+ }
+
+ template <bool IsELF64, typename T> void writeELFXword(T Value) {
+ if (IsELF64)
+ writeLE64(Value);
+ else
+ writeLE32(Value);
+ }
+
+ void writeBytes(llvm::StringRef Bytes) { Out << Bytes; }
+
+ void writeZeroPadding(SizeT N) {
+ static const char Zeros[16] = {0};
+
+ for (SizeT i = 0, e = N / 16; i != e; ++i)
+ Out << llvm::StringRef(Zeros, 16);
+
+ Out << llvm::StringRef(Zeros, N % 16);
+ }
+
+ uint64_t tell() const { return Out.tell(); }
+
+ void seek(uint64_t Off) { Out.seek(Off); }
+
+private:
+ Fdstream &Out;
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEELFSTREAMER_H
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index 8b345d4..095f288 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -103,17 +103,20 @@
UndefPool Undefs;
};
-GlobalContext::GlobalContext(llvm::raw_ostream *OsDump,
- llvm::raw_ostream *OsEmit, VerboseMask Mask,
+GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
+ ELFStreamer *ELFStr, VerboseMask Mask,
TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags)
: StrDump(OsDump), StrEmit(OsEmit), VMask(Mask),
ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt),
TestPrefix(TestPrefix), Flags(Flags), HasEmittedFirstMethod(false),
- RNG("") {
+ RNG(""), ObjectWriter() {
// Pre-register built-in stack names.
newTimerStackID("Total across all functions");
newTimerStackID("Per-function summary");
+ if (Flags.UseELFWriter) {
+ ObjectWriter.reset(new ELFObjectWriter(*this, *ELFStr));
+ }
}
// Scan a string for S[0-9A-Z]*_ patterns and replace them with
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index 02e216b..a082625 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -18,10 +18,10 @@
#include <memory>
#include "llvm/Support/Allocator.h"
-#include "llvm/Support/raw_ostream.h"
#include "IceDefs.h"
#include "IceClFlags.h"
+#include "IceELFObjectWriter.h"
#include "IceIntrinsics.h"
#include "IceRNG.h"
#include "IceTimerTree.h"
@@ -74,7 +74,7 @@
GlobalContext &operator=(const GlobalContext &) = delete;
public:
- GlobalContext(llvm::raw_ostream *OsDump, llvm::raw_ostream *OsEmit,
+ GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer,
VerboseMask Mask, TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags);
~GlobalContext();
@@ -158,6 +158,8 @@
// translation.
RandomNumberGenerator &getRNG() { return RNG; }
+ ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); }
+
// Reset stats at the beginning of a function.
void resetStats() { StatsFunction.reset(); }
void dumpStats(const IceString &Name, bool Final = false);
@@ -212,6 +214,7 @@
const ClFlags &Flags;
bool HasEmittedFirstMethod;
RandomNumberGenerator RNG;
+ std::unique_ptr<ELFObjectWriter> ObjectWriter;
CodeStats StatsFunction;
CodeStats StatsCumulative;
std::vector<TimerStack> Timers;
diff --git a/src/IceMemoryRegion.cpp b/src/IceMemoryRegion.cpp
deleted file mode 100644
index ccfd3cf..0000000
--- a/src/IceMemoryRegion.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-//===- subzero/src/IceMemoryRegion.cpp - Memory region --------------------===//
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-//
-// Modified by the Subzero authors.
-//
-//===----------------------------------------------------------------------===//
-//
-// The Subzero Code Generator
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file defines the MemoryRegion class. It tracks a pointer plus its
-// bounds for bounds-checking in debug mode.
-//===----------------------------------------------------------------------===//
-
-#include "IceMemoryRegion.h"
-
-namespace Ice {
-
-void MemoryRegion::CopyFrom(uintptr_t offset, const MemoryRegion &from) const {
- assert(from.pointer() != NULL && from.size() > 0);
- assert(this->size() >= from.size());
- assert(offset <= this->size() - from.size());
- memmove(reinterpret_cast<void *>(start() + offset), from.pointer(),
- from.size());
-}
-
-} // end of namespace Ice
diff --git a/src/IceMemoryRegion.h b/src/IceMemoryRegion.h
deleted file mode 100644
index b1069a9..0000000
--- a/src/IceMemoryRegion.h
+++ /dev/null
@@ -1,90 +0,0 @@
-//===- subzero/src/IceMemoryRegion.h - Memory region ------------*- C++ -*-===//
-// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-//
-// Modified by the Subzero authors.
-//
-//===----------------------------------------------------------------------===//
-//
-// The Subzero Code Generator
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// This file declares the MemoryRegion class. It tracks a pointer
-// plus its bounds for bounds-checking in debug mode.
-//===----------------------------------------------------------------------===//
-
-#ifndef SUBZERO_SRC_ICE_MEMORY_REGION_H_
-#define SUBZERO_SRC_ICE_MEMORY_REGION_H_
-
-#include "IceDefs.h"
-#include "IceUtils.h"
-
-namespace Ice {
-
-// Memory regions are useful for accessing memory with bounds check in
-// debug mode. They can be safely passed by value and do not assume ownership
-// of the region.
-class MemoryRegion {
-public:
- MemoryRegion(const MemoryRegion &other) = default;
- MemoryRegion &operator=(const MemoryRegion &other) = default;
- MemoryRegion() : pointer_(NULL), size_(0) {}
- MemoryRegion(void *pointer, size_t size) : pointer_(pointer), size_(size) {}
-
- void *pointer() const { return pointer_; }
- size_t size() const { return size_; }
-
- size_t start() const { return reinterpret_cast<size_t>(pointer_); }
- size_t end() const { return start() + size_; }
-
- template <typename T> T Load(size_t offset) const {
- return *ComputeInternalPointer<T>(offset);
- }
-
- template <typename T> void Store(size_t offset, T value) const {
- *ComputeInternalPointer<T>(offset) = value;
- }
-
- template <typename T> T *PointerTo(size_t offset) const {
- return ComputeInternalPointer<T>(offset);
- }
-
- bool Contains(size_t address) const {
- return (address >= start()) && (address < end());
- }
-
- void CopyFrom(size_t offset, const MemoryRegion &from) const;
-
- // Compute a sub memory region based on an existing one.
- void Subregion(const MemoryRegion &from, size_t offset, size_t size) {
- assert(from.size() >= size);
- assert(offset <= (from.size() - size));
- pointer_ = reinterpret_cast<void *>(from.start() + offset);
- size_ = size;
- }
-
- // Compute an extended memory region based on an existing one.
- void Extend(const MemoryRegion ®ion, size_t extra) {
- pointer_ = region.pointer();
- size_ = (region.size() + extra);
- }
-
-private:
- template <typename T> T *ComputeInternalPointer(size_t offset) const {
- assert(size() >= sizeof(T));
- assert(offset <= size() - sizeof(T));
- return reinterpret_cast<T *>(start() + offset);
- }
-
- void *pointer_;
- size_t size_;
-};
-
-} // end of namespace Ice
-
-#endif // SUBZERO_SRC_ICE_MEMORY_REGION_H_
diff --git a/src/IceTargetLowering.h b/src/IceTargetLowering.h
index da1d3f0..3e2243a 100644
--- a/src/IceTargetLowering.h
+++ b/src/IceTargetLowering.h
@@ -24,8 +24,6 @@
namespace Ice {
-typedef uint8_t AsmCodeByte;
-
class Assembler;
// LoweringContext makes it easy to iterate through non-deleted
@@ -165,8 +163,6 @@
virtual bool hasFramePointer() const { return false; }
virtual SizeT getFrameOrStackReg() const = 0;
virtual size_t typeWidthInBytesOnStack(Type Ty) const = 0;
- virtual SizeT getBundleAlignLog2Bytes() const = 0;
- virtual llvm::ArrayRef<AsmCodeByte> getNonExecBundlePadding() const = 0;
bool hasComputedFrame() const { return HasComputedFrame; }
bool shouldDoNopInsertion() const;
// Returns true if this function calls a function that has the
diff --git a/src/IceTargetLoweringX8632.cpp b/src/IceTargetLoweringX8632.cpp
index 937a15b..3fcf8a6 100644
--- a/src/IceTargetLoweringX8632.cpp
+++ b/src/IceTargetLoweringX8632.cpp
@@ -4555,6 +4555,9 @@
: TargetGlobalInitLowering(Ctx) {}
void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) {
+ // TODO(jvoung): handle this without text.
+ if (Ctx->getFlags().UseELFWriter)
+ return;
Ostream &Str = Ctx->getStrEmit();
diff --git a/src/IceTargetLoweringX8632.h b/src/IceTargetLoweringX8632.h
index 920d4cd..2846ac9 100644
--- a/src/IceTargetLoweringX8632.h
+++ b/src/IceTargetLoweringX8632.h
@@ -52,11 +52,6 @@
// i8, and i16 are rounded up to 4 bytes.
return (typeWidthInBytes(Ty) + 3) & ~3;
}
- SizeT getBundleAlignLog2Bytes() const override { return 5; }
- llvm::ArrayRef<AsmCodeByte> getNonExecBundlePadding() const override {
- static const AsmCodeByte Padding[] = { 0xF4 };
- return llvm::ArrayRef<AsmCodeByte>(Padding, 1);
- }
void emitVariable(const Variable *Var) const override;
void lowerArguments() override;
void addProlog(CfgNode *Node) override;
diff --git a/src/IceTranslator.cpp b/src/IceTranslator.cpp
index 2a50a41..a6acfb5 100644
--- a/src/IceTranslator.cpp
+++ b/src/IceTranslator.cpp
@@ -97,8 +97,13 @@
}
void Translator::emitConstants() {
- if (!Ctx->getFlags().DisableTranslation && Func)
- Func->getTarget()->emitConstants();
+ if (!Ctx->getFlags().DisableTranslation && Func) {
+ if (Ctx->getFlags().UseELFWriter) {
+ // TODO(jvoung): create the rodata.cst.{4,8} sections for UseELFWriter.
+ } else {
+ Func->getTarget()->emitConstants();
+ }
+ }
}
void Translator::lowerGlobals(
diff --git a/src/IceTypes.cpp b/src/IceTypes.cpp
index 455aa0c..e6dbadf 100644
--- a/src/IceTypes.cpp
+++ b/src/IceTypes.cpp
@@ -19,8 +19,8 @@
namespace {
const char *TargetArchName[] = {
-#define X(tag, str) str,
- TARGETARCH_TABLE
+#define X(tag, str, is_elf64, e_machine, e_flags) str,
+ TARGETARCH_TABLE
#undef X
};
diff --git a/src/IceTypes.def b/src/IceTypes.def
index 3d4c9b1..0b85ad6 100644
--- a/src/IceTypes.def
+++ b/src/IceTypes.def
@@ -15,13 +15,18 @@
#ifndef SUBZERO_SRC_ICETYPES_DEF
#define SUBZERO_SRC_ICETYPES_DEF
-#define TARGETARCH_TABLE \
- /* enum value, printable string */ \
- X(Target_X8632, "x86-32") \
- X(Target_X8664, "x86-64") \
- X(Target_ARM32, "arm32") \
- X(Target_ARM64, "arm64") \
-//#define X(tag, str)
+// Attributes of each target architecture.
+// NOTE on is_elf64 -- At some point NaCl would like to use ELF32 for all
+// ILP32 sandboxes, but for now the 64-bit architectures use ELF64:
+// https://code.google.com/p/nativeclient/issues/detail?id=349
+// TODO: Whoever adds AArch64 will need to set ABI e_flags.
+#define TARGETARCH_TABLE \
+ /* enum value, printable string, is_elf64, e_machine, e_flags */ \
+ X(Target_X8632, "x86-32", false, EM_386, 0) \
+ X(Target_X8664, "x86-64", true, EM_X86_64, 0) \
+ X(Target_ARM32, "arm32", false, EM_ARM, EF_ARM_EABI_VER5) \
+ X(Target_ARM64, "arm64", true, EM_AARCH64, 0) \
+ //#define X(tag, str, is_elf64, e_machine, e_flags)
#define ICETYPE_TABLE \
/* enum value, size, align, # elts, element type, printable string */ \
diff --git a/src/IceTypes.h b/src/IceTypes.h
index 58f0aa4..662a31a 100644
--- a/src/IceTypes.h
+++ b/src/IceTypes.h
@@ -28,7 +28,7 @@
};
enum TargetArch {
-#define X(tag, str) tag,
+#define X(tag, str, is_elf64, e_machine, e_flags) tag,
TARGETARCH_TABLE
#undef X
TargetArch_NUM
diff --git a/src/assembler.cpp b/src/assembler.cpp
index b0bd297..a9516e8 100644
--- a/src/assembler.cpp
+++ b/src/assembler.cpp
@@ -20,7 +20,6 @@
#include "assembler.h"
#include "IceGlobalContext.h"
-#include "IceMemoryRegion.h"
#include "IceOperand.h"
namespace Ice {
@@ -76,25 +75,6 @@
AssemblerBuffer::~AssemblerBuffer() {}
-void AssemblerBuffer::ProcessFixups(const MemoryRegion ®ion) {
- for (SizeT I = 0; I < fixups_.size(); ++I) {
- AssemblerFixup *fixup = fixups_[I];
- fixup->Process(region, fixup->position());
- }
-}
-
-void AssemblerBuffer::FinalizeInstructions(const MemoryRegion &instructions) {
- // Copy the instructions from the buffer.
- MemoryRegion from(reinterpret_cast<void *>(contents()), Size());
- instructions.CopyFrom(0, from);
-
- // Process fixups in the instructions.
- ProcessFixups(instructions);
-#ifndef NDEBUG
- fixups_processed_ = true;
-#endif // !NDEBUG
-}
-
void AssemblerBuffer::ExtendCapacity() {
intptr_t old_size = Size();
intptr_t old_capacity = Capacity();
@@ -123,6 +103,11 @@
assert(Size() == old_size);
}
+llvm::StringRef Assembler::getBufferView() const {
+ return llvm::StringRef(reinterpret_cast<const char *>(buffer_.contents()),
+ buffer_.Size());
+}
+
void Assembler::emitIASBytes(GlobalContext *Ctx) const {
Ostream &Str = Ctx->getStrEmit();
intptr_t EndPosition = buffer_.Size();
diff --git a/src/assembler.h b/src/assembler.h
index 37963c4..dc70668 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -35,7 +35,6 @@
class AssemblerFixup;
class AssemblerBuffer;
class ConstantRelocatable;
-class MemoryRegion;
// Assembler fixups are positions in generated code that hold relocation
// information that needs to be processed before finalizing the code
@@ -45,7 +44,6 @@
AssemblerFixup &operator=(const AssemblerFixup &) = delete;
public:
- virtual void Process(const MemoryRegion ®ion, intptr_t position) = 0;
// It would be ideal if the destructor method could be made private,
// but the g++ compiler complains when this is subclassed.
@@ -109,12 +107,6 @@
intptr_t Size() const { return cursor_ - contents_; }
uintptr_t contents() const { return contents_; }
- // Copy the assembled instructions into the specified memory block
- // and apply all fixups.
- // TODO(jvoung): This will be different. We'll be writing the text
- // and reloc section to a file?
- void FinalizeInstructions(const MemoryRegion ®ion);
-
// To emit an instruction to the assembler buffer, the EnsureCapacity helper
// must be used to guarantee that the underlying data area is big enough to
// hold the emitted instruction. Usage:
@@ -189,9 +181,6 @@
return (limit_ - contents_) + kMinimumGap;
}
- // Process the fixup chain.
- void ProcessFixups(const MemoryRegion ®ion);
-
// Compute the limit based on the data area and the capacity. See
// description of kMinimumGap for the reasoning behind the value.
static uintptr_t ComputeLimit(uintptr_t data, intptr_t capacity) {
@@ -226,8 +215,20 @@
// Allocate data of type T using the per-Assembler allocator.
template <typename T> T *Allocate() { return Allocator.Allocate<T>(); }
+ // Align the tail end of the function to the required target alignment.
+ virtual void alignFunction() = 0;
+
+ virtual SizeT getBundleAlignLog2Bytes() const = 0;
+
+ virtual llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const = 0;
+
+ // Mark the current text location as the start of a CFG node
+ // (represented by NodeNumber).
virtual void BindCfgNodeLabel(SizeT NodeNumber) = 0;
+ // Return a view of all the bytes of code for the current function.
+ llvm::StringRef getBufferView() const;
+
void emitIASBytes(GlobalContext *Ctx) const;
private:
diff --git a/src/assembler_ia32.cpp b/src/assembler_ia32.cpp
index 94263e8..30b84e8 100644
--- a/src/assembler_ia32.cpp
+++ b/src/assembler_ia32.cpp
@@ -20,7 +20,6 @@
#include "assembler_ia32.h"
#include "IceCfg.h"
-#include "IceMemoryRegion.h"
#include "IceOperand.h"
namespace Ice {
@@ -37,13 +36,6 @@
DirectCallRelocation(Kind, Sym);
}
- void Process(const MemoryRegion ®ion, intptr_t position) override {
- // Direct calls are relative to the following instruction on x86.
- int32_t pointer = region.Load<int32_t>(position);
- int32_t delta = region.start() + position + sizeof(int32_t);
- region.Store<int32_t>(position, pointer - delta);
- }
-
private:
DirectCallRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
: AssemblerFixup(Kind, Sym) {}
@@ -77,6 +69,22 @@
#endif
}
+void AssemblerX86::alignFunction() {
+ intptr_t Pos = buffer_.GetPosition();
+ SizeT Align = 1 << getBundleAlignLog2Bytes();
+ intptr_t Mod = Pos & (Align - 1);
+ if (Mod == 0) {
+ return;
+ }
+ SizeT BytesNeeded = Align - Mod;
+ const SizeT HltSize = 1;
+ while (BytesNeeded > 0) {
+ hlt();
+ BytesNeeded -= HltSize;
+ }
+ assert((buffer_.GetPosition() & (Align - 1)) == 0);
+}
+
Label *AssemblerX86::GetOrCreateLabel(SizeT Number, LabelVector &Labels) {
Label *L = nullptr;
if (Number == Labels.size()) {
diff --git a/src/assembler_ia32.h b/src/assembler_ia32.h
index a7ffc9e..d38dca7 100644
--- a/src/assembler_ia32.h
+++ b/src/assembler_ia32.h
@@ -56,12 +56,6 @@
DisplacementRelocation(Kind, Sym);
}
- void Process(const MemoryRegion ®ion, intptr_t position) override {
- (void)region;
- (void)position;
- llvm_unreachable("We might not be using this Process() method later.");
- }
-
private:
DisplacementRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
: AssemblerFixup(Kind, Sym) {}
@@ -374,6 +368,15 @@
static const bool kNearJump = true;
static const bool kFarJump = false;
+ void alignFunction() override;
+
+ SizeT getBundleAlignLog2Bytes() const override { return 5; }
+
+ llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const override {
+ static const uint8_t Padding[] = {0xF4};
+ return llvm::ArrayRef<uint8_t>(Padding, 1);
+ }
+
Label *GetOrCreateCfgNodeLabel(SizeT NodeNumber);
void BindCfgNodeLabel(SizeT NodeNumber) override;
Label *GetOrCreateLocalLabel(SizeT Number);
@@ -825,10 +828,6 @@
intptr_t CodeSize() const { return buffer_.Size(); }
- void FinalizeInstructions(const MemoryRegion ®ion) {
- buffer_.FinalizeInstructions(region);
- }
-
private:
inline void EmitUint8(uint8_t value);
inline void EmitInt16(int16_t value);
diff --git a/src/llvm2ice.cpp b/src/llvm2ice.cpp
index a28b07f..4caa6b8 100644
--- a/src/llvm2ice.cpp
+++ b/src/llvm2ice.cpp
@@ -20,6 +20,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/SourceMgr.h"
@@ -173,6 +174,11 @@
static cl::alias UseIas("ias", cl::desc("Alias for -integrated-as"),
cl::NotHidden, cl::aliasopt(UseIntegratedAssembler));
+static cl::opt<bool>
+ UseELFWriter("elf-writer",
+ cl::desc("Use ELF writer with the integrated assembler"),
+ cl::init(false));
+
static cl::opt<bool> AlwaysExitSuccess(
"exit-success", cl::desc("Exit with success status, even if errors found"),
cl::init(false));
@@ -200,7 +206,7 @@
// Validates values of build attributes. Prints them to Stream if
// Stream is non-null.
-static void ValidateAndGenerateBuildAttributes(raw_os_ostream *Stream) {
+static void ValidateAndGenerateBuildAttributes(Ice::Ostream *Stream) {
if (Stream)
*Stream << TargetArch << "\n";
@@ -242,25 +248,20 @@
VMask |= VerboseList[i];
}
- std::ofstream Ofs;
- if (OutputFilename != "-") {
- Ofs.open(OutputFilename.c_str(), std::ofstream::out);
- }
- raw_os_ostream *Os =
- new raw_os_ostream(OutputFilename == "-" ? std::cout : Ofs);
- Os->SetUnbuffered();
-
- ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Os : nullptr);
- if (GenerateBuildAtts)
- return GetReturnValue(0);
-
std::ofstream Lfs;
+ std::unique_ptr<Ice::Ostream> Ls;
if (LogFilename != "-") {
Lfs.open(LogFilename.c_str(), std::ofstream::out);
+ Ls.reset(new raw_os_ostream(Lfs));
+ } else {
+ Ls.reset(new raw_os_ostream(std::cout));
}
- raw_os_ostream *Ls = new raw_os_ostream(LogFilename == "-" ? std::cout : Lfs);
Ls->SetUnbuffered();
+ ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr);
+ if (GenerateBuildAtts)
+ return GetReturnValue(0);
+
if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) {
*Ls << "Error: Build doesn't allow --no-ir-gen when not "
<< "ALLOW_DISABLE_IR_GEN!\n";
@@ -274,6 +275,7 @@
Flags.FunctionSections = FunctionSections;
Flags.DataSections = DataSections;
Flags.UseIntegratedAssembler = UseIntegratedAssembler;
+ Flags.UseELFWriter = UseELFWriter;
Flags.UseSandboxing = UseSandboxing;
Flags.PhiEdgeSplit = EnablePhiEdgeSplit;
Flags.DecorateAsm = DecorateAsm;
@@ -294,11 +296,45 @@
LLSuffix.length(), LLSuffix) == 0)
BuildOnRead = false;
- Ice::GlobalContext Ctx(Ls, Os, VMask, TargetArch, OptLevel, TestPrefix,
- Flags);
+ // With the ELF writer, use a raw_fd_ostream to allow seeking.
+ // Also don't buffer, otherwise it gets pretty slow.
+ std::unique_ptr<Ice::Ostream> Os;
+ std::unique_ptr<Ice::ELFStreamer> ELFStr;
+ std::ofstream Ofs;
+ if (UseELFWriter) {
+ if (OutputFilename == "-") {
+ *Ls << "Error: writing binary ELF to stdout is unsupported\n";
+ return GetReturnValue(1);
+ }
+ std::string ErrorInfo;
+ raw_fd_ostream *FdOs =
+ new raw_fd_ostream(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None);
+ Os.reset(FdOs);
+ if (!ErrorInfo.empty()) {
+ *Ls << "Failed to open output file: " << OutputFilename << ":\n"
+ << ErrorInfo << "\n";
+ return GetReturnValue(1);
+ }
+ ELFStr.reset(new Ice::ELFStreamer(*FdOs));
+ } else {
+ if (OutputFilename != "-") {
+ Ofs.open(OutputFilename.c_str(), std::ofstream::out);
+ Os.reset(new raw_os_ostream(Ofs));
+ } else {
+ Os.reset(new raw_os_ostream(std::cout));
+ }
+ Os->SetUnbuffered();
+ }
+
+ Ice::GlobalContext Ctx(Ls.get(), Os.get(), ELFStr.get(), VMask, TargetArch,
+ OptLevel, TestPrefix, Flags);
Ice::TimerMarker T(Ice::TimerStack::TT_szmain, &Ctx);
+ if (UseELFWriter) {
+ Ctx.getObjectWriter()->writeInitialELFHeader();
+ }
+
int ErrorStatus = 0;
if (BuildOnRead) {
Ice::PNaClTranslator Translator(&Ctx, Flags);
@@ -324,6 +360,9 @@
<< "--build-on-read=0 not allowed\n";
return GetReturnValue(1);
}
+ if (UseELFWriter) {
+ Ctx.getObjectWriter()->writeNonUserSections();
+ }
if (SubzeroTimingEnabled)
Ctx.dumpTimers();
if (TimeEachFunction) {
diff --git a/tests_lit/llvm2ice_tests/elf_container.ll b/tests_lit/llvm2ice_tests/elf_container.ll
new file mode 100644
index 0000000..3b6f700
--- /dev/null
+++ b/tests_lit/llvm2ice_tests/elf_container.ll
@@ -0,0 +1,242 @@
+; Tests that we generate an ELF container with fields that make sense,
+; cross-validating against llvm-mc.
+
+; For the integrated ELF writer, we can't pipe the output because we need
+; to seek backward and patch up the file headers. So, use a temporary file.
+; RUN: %p2i -i %s --args -O2 --verbose none -elf-writer -o %t \
+; RUN: && llvm-readobj -file-headers -sections -section-data \
+; RUN: -relocations -symbols %t | FileCheck %s
+
+; RUN: %p2i -i %s --args -O2 --verbose none \
+; RUN: | llvm-mc -triple=i686-none-nacl -filetype=obj -o - \
+; RUN: | llvm-readobj -file-headers -sections -section-data \
+; RUN: -relocations -symbols - | FileCheck %s
+
+; Use intrinsics to test external calls.
+declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i32, i1)
+declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i32, i1)
+
+; Test some global data relocs (data, rodata, bss).
+@bytes = internal global [7 x i8] c"abcdefg", align 1
+@const_bytes = internal constant [7 x i8] c"abcdefg", align 1
+
+@ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32), align 4
+@const_ptr = internal constant i32 ptrtoint ([7 x i8]* @bytes to i32), align 4
+
+@ptr_to_func = internal global i32 ptrtoint (double ()* @returnDoubleConst to i32), align 4
+@const_ptr_to_func = internal constant i32 ptrtoint (double ()* @returnDoubleConst to i32), align 4
+
+@short_zero = internal global [2 x i8] zeroinitializer, align 2
+@double_zero = internal global [8 x i8] zeroinitializer, align 8
+@const_short_zero = internal constant [2 x i8] zeroinitializer, align 2
+@const_double_zero = internal constant [8 x i8] zeroinitializer, align 8
+
+
+@addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 128)
+@const_addend_ptr = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 64)
+
+; Use float/double constants to test constant pools.
+define internal float @returnFloatConst() {
+entry:
+ %f = fadd float 0x3FF3AE1480000000, 0x3FF3AE1400000000
+ ret float %f
+}
+
+define internal double @returnDoubleConst() {
+entry:
+ %d = fadd double 1.230000e+00, 3.210000e+00
+ ret double %d
+}
+
+define internal void @test_memcpy(i32 %iptr_dst, i32 %len) {
+entry:
+ %dst = inttoptr i32 %iptr_dst to i8*
+ %src = bitcast [7 x i8]* @bytes to i8*
+ call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %src,
+ i32 %len, i32 1, i1 false)
+ ret void
+}
+
+define internal void @test_memset(i32 %iptr_dst, i32 %wide_val, i32 %len) {
+entry:
+ %val = trunc i32 %wide_val to i8
+ %dst = inttoptr i32 %iptr_dst to i8*
+ call void @llvm.memset.p0i8.i32(i8* %dst, i8 %val,
+ i32 %len, i32 1, i1 false)
+ ret void
+}
+
+; Test non-internal functions too.
+define void @_start(i32) {
+ %f = call float @returnFloatConst()
+ %d = call double @returnDoubleConst()
+ call void @test_memcpy(i32 0, i32 99)
+ call void @test_memset(i32 0, i32 0, i32 99)
+ ret void
+}
+
+
+; CHECK: ElfHeader {
+; CHECK: Ident {
+; CHECK: Magic: (7F 45 4C 46)
+; CHECK: Class: 32-bit
+; CHECK: DataEncoding: LittleEndian
+; CHECK: OS/ABI: SystemV (0x0)
+; CHECK: ABIVersion: 0
+; CHECK: Unused: (00 00 00 00 00 00 00)
+; CHECK: }
+; CHECK: Type: Relocatable (0x1)
+; CHECK: Machine: EM_386 (0x3)
+; CHECK: Version: 1
+; CHECK: Entry: 0x0
+; CHECK: ProgramHeaderOffset: 0x0
+; CHECK: SectionHeaderOffset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK: Flags [ (0x0)
+; CHECK: ]
+; CHECK: HeaderSize: 52
+; CHECK: ProgramHeaderEntrySize: 0
+; CHECK: ProgramHeaderCount: 0
+; CHECK: SectionHeaderEntrySize: 40
+; CHECK: SectionHeaderCount: {{[1-9][0-9]*}}
+; CHECK: StringTableSectionIndex: {{[1-9][0-9]*}}
+; CHECK: }
+
+
+; CHECK: Sections [
+; CHECK: Section {
+; CHECK: Index: 0
+; CHECK: Name: (0)
+; CHECK: Type: SHT_NULL
+; CHECK: Flags [ (0x0)
+; CHECK: ]
+; CHECK: Address: 0x0
+; CHECK: Offset: 0x0
+; CHECK: Size: 0
+; CHECK: Link: 0
+; CHECK: Info: 0
+; CHECK: AddressAlignment: 0
+; CHECK: EntrySize: 0
+; CHECK: SectionData (
+; CHECK-NEXT: )
+; CHECK: }
+; CHECK: Section {
+; CHECK: Index: {{[1-9][0-9]*}}
+; CHECK: Name: .text
+; CHECK: Type: SHT_PROGBITS
+; CHECK: Flags [ (0x6)
+; CHECK: SHF_ALLOC
+; CHECK: SHF_EXECINSTR
+; CHECK: ]
+; CHECK: Address: 0x0
+; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK: Size: {{[1-9][0-9]*}}
+; CHECK: Link: 0
+; CHECK: Info: 0
+; CHECK: AddressAlignment: 32
+; CHECK: EntrySize: 0
+; CHECK: SectionData (
+; There's probably halt padding (0xF4) in there somewhere.
+; CHECK: {{.*}}F4
+; CHECK: )
+; CHECK: }
+; CHECK: Section {
+; CHECK: Index: {{[1-9][0-9]*}}
+; CHECK: Name: .shstrtab
+; CHECK: Type: SHT_STRTAB
+; CHECK: Flags [ (0x0)
+; CHECK: ]
+; CHECK: Address: 0x0
+; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK: Size: {{[1-9][0-9]*}}
+; CHECK: Link: 0
+; CHECK: Info: 0
+; CHECK: AddressAlignment: 1
+; CHECK: EntrySize: 0
+; CHECK: SectionData (
+; CHECK: {{.*}}.text{{.*}}
+; CHECK: )
+; CHECK: }
+; CHECK: Section {
+; CHECK: Index: {{[1-9][0-9]*}}
+; CHECK: Name: .symtab
+; CHECK: Type: SHT_SYMTAB
+; CHECK: Flags [ (0x0)
+; CHECK: ]
+; CHECK: Address: 0x0
+; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK: Size: {{[1-9][0-9]*}}
+; CHECK: Link: [[STRTAB_INDEX:[1-9][0-9]*]]
+; CHECK: Info: [[GLOBAL_START_INDEX:[1-9][0-9]*]]
+; CHECK: AddressAlignment: 4
+; CHECK: EntrySize: 16
+; CHECK: }
+; CHECK: Section {
+; CHECK: Index: [[STRTAB_INDEX]]
+; CHECK: Name: .strtab
+; CHECK: Type: SHT_STRTAB
+; CHECK: Flags [ (0x0)
+; CHECK: ]
+; CHECK: Address: 0x0
+; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK: Size: {{[1-9][0-9]*}}
+; CHECK: Link: 0
+; CHECK: Info: 0
+; CHECK: AddressAlignment: 1
+; CHECK: EntrySize: 0
+; CHECK: }
+
+
+; CHECK: Relocations [
+; TODO: fill it out.
+; CHECK: ]
+
+
+; CHECK: Symbols [
+; CHECK-NEXT: Symbol {
+; CHECK-NEXT: Name: (0)
+; CHECK-NEXT: Value: 0x0
+; CHECK-NEXT: Size: 0
+; CHECK-NEXT: Binding: Local
+; CHECK-NEXT: Type: None
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: Undefined (0x0)
+; CHECK-NEXT: }
+; TODO: fill in the data symbols.
+; CHECK: Symbol {
+; CHECK: Name: returnDoubleConst
+; CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK-NEXT: Size: 0
+; CHECK-NEXT: Binding: Local
+; CHECK-NEXT: Type: None
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .text
+; CHECK-NEXT: }
+; CHECK: Symbol {
+; CHECK: Name: returnFloatConst
+; This happens to be the first function, so its offset is 0 within the text.
+; CHECK-NEXT: Value: 0x0
+; CHECK-NEXT: Size: 0
+; CHECK-NEXT: Binding: Local
+; CHECK-NEXT: Type: None
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .text
+; CHECK-NEXT: }
+; CHECK: Symbol {
+; CHECK: Name: test_memcpy
+; CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK-NEXT: Size: 0
+; CHECK-NEXT: Binding: Local
+; CHECK-NEXT: Type: None
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .text
+; CHECK-NEXT: }
+; CHECK: Symbol {
+; CHECK: Name: _start
+; CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}}
+; CHECK-NEXT: Size: 0
+; CHECK-NEXT: Binding: Global
+; CHECK-NEXT: Type: Function
+; CHECK-NEXT: Other: 0
+; CHECK-NEXT: Section: .text
+; CHECK-NEXT: }
+; CHECK: ]
diff --git a/unittest/IceELFSectionTest.cpp b/unittest/IceELFSectionTest.cpp
new file mode 100644
index 0000000..8dbcc65
--- /dev/null
+++ b/unittest/IceELFSectionTest.cpp
@@ -0,0 +1,134 @@
+//===- unittest/IceELFSectionTest.cpp - ELF Section unit tests ------------===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include <algorithm>
+
+#include "gtest/gtest.h"
+
+#include "IceDefs.h"
+#include "IceELFSection.h"
+
+namespace Ice {
+namespace {
+
+// Test string table layout for various permutations. Test that pop,
+// lollipop, and lipop are able to share data, while the other strings do not.
+void CheckStringTablePermLayout(const ELFStringTableSection &Strtab) {
+ size_t pop_index = Strtab.getIndex("pop");
+ size_t pop_size = IceString("pop").size();
+ size_t lollipop_index = Strtab.getIndex("lollipop");
+ size_t lollipop_size = IceString("lollipop").size();
+ size_t lipop_index = Strtab.getIndex("lipop");
+ size_t lipop_size = IceString("lipop").size();
+ size_t pops_index = Strtab.getIndex("pops");
+ size_t pops_size = IceString("pops").size();
+ size_t unpop_index = Strtab.getIndex("unpop");
+ size_t unpop_size = IceString("unpop").size();
+ size_t popular_index = Strtab.getIndex("popular");
+ size_t popular_size = IceString("popular").size();
+ size_t strtab_index = Strtab.getIndex(".strtab");
+ size_t strtab_size = IceString(".strtab").size();
+ size_t shstrtab_index = Strtab.getIndex(".shstrtab");
+ size_t shstrtab_size = IceString(".shstrtab").size();
+ size_t symtab_index = Strtab.getIndex(".symtab");
+ size_t symtab_size = IceString(".symtab").size();
+
+ // Check that some sharing exists.
+ EXPECT_EQ(pop_index, lollipop_index + (lollipop_size - pop_size));
+ EXPECT_EQ(lipop_index, lollipop_index + (lollipop_size - lipop_size));
+
+ // Check that .strtab does not share with .shstrtab (the dot throws it off).
+ EXPECT_NE(strtab_index, shstrtab_index + (shstrtab_size - strtab_size));
+
+ // Check contents make sense.
+ EXPECT_EQ(Strtab.getSectionData().slice(pop_index, pop_index + pop_size),
+ llvm::StringRef("pop"));
+ EXPECT_EQ(Strtab.getSectionData().slice(lollipop_index,
+ lollipop_index + lollipop_size),
+ llvm::StringRef("lollipop"));
+ EXPECT_EQ(Strtab.getSectionData().slice(pops_index, pops_index + pops_size),
+ llvm::StringRef("pops"));
+ EXPECT_EQ(
+ Strtab.getSectionData().slice(unpop_index, unpop_index + unpop_size),
+ llvm::StringRef("unpop"));
+ EXPECT_EQ(Strtab.getSectionData().slice(popular_index,
+ popular_index + popular_size),
+ llvm::StringRef("popular"));
+ EXPECT_EQ(
+ Strtab.getSectionData().slice(strtab_index, strtab_index + strtab_size),
+ llvm::StringRef(".strtab"));
+ EXPECT_EQ(Strtab.getSectionData().slice(shstrtab_index,
+ shstrtab_index + shstrtab_size),
+ llvm::StringRef(".shstrtab"));
+ EXPECT_EQ(
+ Strtab.getSectionData().slice(symtab_index, symtab_index + symtab_size),
+ llvm::StringRef(".symtab"));
+}
+
+// Test that the order in which strings are added doesn't matter.
+TEST(IceELFSectionTest, StringTableBuilderPermSeveral) {
+ std::vector<IceString> Strings;
+ Strings.push_back("pop");
+ Strings.push_back("lollipop");
+ Strings.push_back("lipop");
+ Strings.push_back("pops");
+ Strings.push_back("unpop");
+ Strings.push_back("popular");
+ Strings.push_back("a");
+ Strings.push_back("z");
+ Strings.push_back("foo");
+ Strings.push_back("bar");
+ Strings.push_back(".text");
+ Strings.push_back(".symtab");
+ Strings.push_back(".strtab");
+ Strings.push_back(".shstrtab");
+ Strings.push_back("_start");
+ const SizeT NumTests = 128;
+ for (SizeT i = 0; i < NumTests; ++i) {
+ std::random_shuffle(Strings.begin(), Strings.end());
+ ELFStringTableSection Strtab(".strtab", SHT_STRTAB, 0, 1, 0);
+ for (auto &S : Strings) {
+ Strtab.add(S);
+ }
+ Strtab.doLayout();
+ CheckStringTablePermLayout(Strtab);
+ }
+}
+
+// Test that adding duplicate strings is fine.
+TEST(IceELFSectionTest, StringTableBuilderDuplicates) {
+ ELFStringTableSection Strtab(".strtab", SHT_STRTAB, 0, 1, 0);
+ Strtab.add("unpop");
+ Strtab.add("pop");
+ Strtab.add("lollipop");
+ Strtab.add("a");
+ Strtab.add("popular");
+ Strtab.add("pops");
+ Strtab.add("lipop");
+ Strtab.add(".strtab");
+ Strtab.add(".shstrtab");
+ Strtab.add(".symtab");
+
+ Strtab.add(".symtab");
+ Strtab.add(".shstrtab");
+ Strtab.add(".strtab");
+ Strtab.add("lipop");
+ Strtab.add("pops");
+ Strtab.add("popular");
+ Strtab.add("a");
+ Strtab.add("lollipop");
+ Strtab.add("pop");
+ Strtab.add("unpop");
+
+ Strtab.doLayout();
+ CheckStringTablePermLayout(Strtab);
+}
+
+} // end of anonymous namespace
+} // end of namespace Ice