//===- LinkerTest.cpp -----------------------------------------------------===// // // The MCLinker Project // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "LinkerTest.h" #include <mcld/Environment.h> #include <mcld/Module.h> #include <mcld/InputTree.h> #include <mcld/IRBuilder.h> #include <mcld/Linker.h> #include <mcld/LinkerConfig.h> #include <mcld/Support/Path.h> #include <llvm/Support/ELF.h> using namespace mcld; using namespace mcld::test; using namespace mcld::sys::fs; // Constructor can do set-up work for all test here. LinkerTest::LinkerTest() { } // Destructor can do clean-up work that doesn't throw exceptions here. LinkerTest::~LinkerTest() { } // SetUp() will be called immediately before each test. void LinkerTest::SetUp() { } // TearDown() will be called immediately after each test. void LinkerTest::TearDown() { } //===----------------------------------------------------------------------===// // Testcases //===----------------------------------------------------------------------===// TEST_F( LinkerTest, set_up_n_clean_up) { Initialize(); LinkerConfig config("arm-none-linux-gnueabi"); Module module("test"); config.setCodeGenType(LinkerConfig::DynObj); Linker linker; linker.config(config); IRBuilder builder(module, config); // create inputs here // builder.CreateInput("./test.o"); if (linker.link(module, builder)) linker.emit("./test.so"); Finalize(); } // %MCLinker --shared -soname=libplasma.so -Bsymbolic // -mtriple="armv7-none-linux-gnueabi" // -L=%p/../../../libs/ARM/Android/android-14 // %p/../../../libs/ARM/Android/android-14/crtbegin_so.o // %p/plasma.o // -lm -llog -ljnigraphics -lc // %p/../../../libs/ARM/Android/android-14/crtend_so.o // -o libplasma.so TEST_F( LinkerTest, plasma) { Initialize(); Linker linker; ///< --mtriple="armv7-none-linux-gnueabi" LinkerConfig config("armv7-none-linux-gnueabi"); /// -L=${TOPDIR}/test/libs/ARM/Android/android-14 Path search_dir(TOPDIR); search_dir.append("test/libs/ARM/Android/android-14"); config.options().directories().insert(search_dir); /// To configure linker before setting options. Linker::config sets up /// default target-dependent configuration to LinkerConfig. linker.config(config); config.setCodeGenType(LinkerConfig::DynObj); ///< --shared config.options().setSOName("libplasma.so"); ///< --soname=libplasma.so config.options().setBsymbolic(); ///< -Bsymbolic Module module("libplasma.so"); IRBuilder builder(module, config); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtbegin_so.o Path crtbegin(search_dir); crtbegin.append("crtbegin_so.o"); builder.ReadInput("crtbegin", crtbegin); /// ${TOPDIR}/test/Android/Plasma/ARM/plasma.o Path plasma(TOPDIR); plasma.append("test/Android/Plasma/ARM/plasma.o"); builder.ReadInput("plasma", plasma); // -lm -llog -ljnigraphics -lc builder.ReadInput("m"); builder.ReadInput("log"); builder.ReadInput("jnigraphics"); builder.ReadInput("c"); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtend_so.o Path crtend(search_dir); crtend.append("crtend_so.o"); builder.ReadInput("crtend", crtend); if (linker.link(module, builder)) { linker.emit("libplasma.so"); ///< -o libplasma.so } Finalize(); } // The outputs generated without -Bsymbolic usually have more relocation // entries than the outputs generated with -Bsymbolic. This testcase generates // output with -Bsymbolic first, then generate the same output without -Bsymbolic. // By this way, we can make sure symbols and relocations are cleaned between // two linkings. TEST_F( LinkerTest, plasma_twice) { Initialize(); Linker linker; ///< --mtriple="armv7-none-linux-gnueabi" LinkerConfig config1("armv7-none-linux-gnueabi"); /// -L=${TOPDIR}/test/libs/ARM/Android/android-14 Path search_dir(TOPDIR); search_dir.append("test/libs/ARM/Android/android-14"); config1.options().directories().insert(search_dir); /// To configure linker before setting options. Linker::config sets up /// default target-dependent configuration to LinkerConfig. linker.config(config1); config1.setCodeGenType(LinkerConfig::DynObj); ///< --shared config1.options().setSOName("libplasma.once.so"); ///< --soname=libplasma.twice.so config1.options().setBsymbolic(false); ///< -Bsymbolic Module module1("libplasma.once.so"); IRBuilder builder1(module1, config1); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtbegin_so.o Path crtbegin(search_dir); crtbegin.append("crtbegin_so.o"); builder1.ReadInput("crtbegin", crtbegin); /// ${TOPDIR}/test/Android/Plasma/ARM/plasma.o Path plasma(TOPDIR); plasma.append("test/Android/Plasma/ARM/plasma.o"); builder1.ReadInput("plasma", plasma); // -lm -llog -ljnigraphics -lc builder1.ReadInput("m"); builder1.ReadInput("log"); builder1.ReadInput("jnigraphics"); builder1.ReadInput("c"); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtend_so.o Path crtend(search_dir); crtend.append("crtend_so.o"); builder1.ReadInput("crtend", crtend); if (linker.link(module1, builder1)) { linker.emit("libplasma.once.so"); ///< -o libplasma.so } Finalize(); linker.reset(); Initialize(); ///< --mtriple="armv7-none-linux-gnueabi" LinkerConfig config2("armv7-none-linux-gnueabi"); /// -L=${TOPDIR}/test/libs/ARM/Android/android-14 config2.options().directories().insert(search_dir); /// To configure linker before setting options. Linker::config sets up /// default target-dependent configuration to LinkerConfig. linker.config(config2); config2.setCodeGenType(LinkerConfig::DynObj); ///< --shared config2.options().setSOName("libplasma.twice.so"); ///< --soname=libplasma.twice.exe config2.options().setBsymbolic(); ///< -Bsymbolic Module module2("libplasma.so"); IRBuilder builder2(module2, config2); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtbegin_so.o builder2.ReadInput("crtbegin", crtbegin); /// ${TOPDIR}/test/Android/Plasma/ARM/plasma.o builder2.ReadInput("plasma", plasma); // -lm -llog -ljnigraphics -lc builder2.ReadInput("m"); builder2.ReadInput("log"); builder2.ReadInput("jnigraphics"); builder2.ReadInput("c"); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtend_so.o builder2.ReadInput("crtend", crtend); if (linker.link(module2, builder2)) { linker.emit("libplasma.twice.so"); ///< -o libplasma.exe } Finalize(); } // This testcase put IRBuilder in the heap TEST_F( LinkerTest, plasma_twice_irbuilder_heap) { Initialize(); Linker linker; ///< --mtriple="armv7-none-linux-gnueabi" LinkerConfig config1("armv7-none-linux-gnueabi"); /// -L=${TOPDIR}/test/libs/ARM/Android/android-14 Path search_dir(TOPDIR); search_dir.append("test/libs/ARM/Android/android-14"); config1.options().directories().insert(search_dir); /// To configure linker before setting options. Linker::config sets up /// default target-dependent configuration to LinkerConfig. linker.config(config1); config1.setCodeGenType(LinkerConfig::DynObj); ///< --shared config1.options().setSOName("libplasma.once.so"); ///< --soname=libplasma.twice.so config1.options().setBsymbolic(false); ///< -Bsymbolic Module module1("libplasma.once.so"); IRBuilder *builder1 = new IRBuilder(module1, config1); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtbegin_so.o Path crtbegin(search_dir); crtbegin.append("crtbegin_so.o"); builder1->ReadInput("crtbegin", crtbegin); /// ${TOPDIR}/test/Android/Plasma/ARM/plasma.o Path plasma(TOPDIR); plasma.append("test/Android/Plasma/ARM/plasma.o"); builder1->ReadInput("plasma", plasma); // -lm -llog -ljnigraphics -lc builder1->ReadInput("m"); builder1->ReadInput("log"); builder1->ReadInput("jnigraphics"); builder1->ReadInput("c"); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtend_so.o Path crtend(search_dir); crtend.append("crtend_so.o"); builder1->ReadInput("crtend", crtend); if (linker.link(module1, *builder1)) { linker.emit("libplasma.once.so"); ///< -o libplasma.so } // Can not delete builder until emit the output. Dynamic string table // needs the file name of the input files, and the inputs' life is // controlled by IRBuilder delete builder1; Finalize(); linker.reset(); Initialize(); ///< --mtriple="armv7-none-linux-gnueabi" LinkerConfig config2("armv7-none-linux-gnueabi"); /// -L=${TOPDIR}/test/libs/ARM/Android/android-14 config2.options().directories().insert(search_dir); /// To configure linker before setting options. Linker::config sets up /// default target-dependent configuration to LinkerConfig. linker.config(config2); config2.setCodeGenType(LinkerConfig::DynObj); ///< --shared config2.options().setSOName("libplasma.twice.so"); ///< --soname=libplasma.twice.exe config2.options().setBsymbolic(); ///< -Bsymbolic Module module2("libplasma.so"); IRBuilder* builder2 = new IRBuilder(module2, config2); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtbegin_so.o builder2->ReadInput("crtbegin", crtbegin); /// ${TOPDIR}/test/Android/Plasma/ARM/plasma.o builder2->ReadInput("plasma", plasma); // -lm -llog -ljnigraphics -lc builder2->ReadInput("m"); builder2->ReadInput("log"); builder2->ReadInput("jnigraphics"); builder2->ReadInput("c"); /// ${TOPDIR}/test/libs/ARM/Android/android-14/crtend_so.o builder2->ReadInput("crtend", crtend); if (linker.link(module2, *builder2)) { linker.emit("libplasma.twice.so"); ///< -o libplasma.exe } delete builder2; Finalize(); } // %MCLinker --shared -soname=libgotplt.so -mtriple arm-none-linux-gnueabi // gotplt.o -o libgotplt.so TEST_F( LinkerTest, plasma_object) { Initialize(); Linker linker; ///< --mtriple="armv7-none-linux-gnueabi" LinkerConfig config("armv7-none-linux-gnueabi"); /// To configure linker before setting options. Linker::config sets up /// default target-dependent configuration to LinkerConfig. linker.config(config); config.setCodeGenType(LinkerConfig::DynObj); ///< --shared config.options().setSOName("libgotplt.so"); ///< --soname=libgotplt.so Module module; IRBuilder builder(module, config); Path gotplt_o(TOPDIR); gotplt_o.append("test/PLT/gotplt.o"); Input* input = builder.CreateInput("gotplt.o", gotplt_o, Input::Object); /// Sections /// [ 0] NULL 00000000 000000 000000 00 0 0 0 builder.CreateELFHeader(*input, "", LDFileFormat::Null, llvm::ELF::SHT_NULL, 0x0); /// [ 1] .text PROGBITS 00000000 000034 000010 00 AX 0 0 4 LDSection* text = builder.CreateELFHeader(*input, ".text", llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_EXECINSTR, 4); SectionData* text_data = builder.CreateSectionData(*text); static uint8_t text_content[] = { 0x00, 0x48, 0x2d, 0xe9, 0xfe, 0xff, 0xff, 0xeb, 0x00, 0x48, 0xbd, 0xe8, 0x0e, 0xf0, 0xa0, 0xe1 }; Fragment* text_frag = builder.CreateRegion(text_content, 0x10); builder.AppendFragment(*text_frag, *text_data); /// [ 2] .rel.text REL 00000000 0002ac 000008 08 7 1 4 LDSection* rel_text = builder.CreateELFHeader(*input, ".rel.text", llvm::ELF::SHT_REL, 0x0, 4); rel_text->setLink(text); builder.CreateRelocData(*rel_text); /// [ 3] .data PROGBITS 00000000 000044 000000 00 WA 0 0 4 LDSection* data = builder.CreateELFHeader(*input, ".data", llvm::ELF::SHT_PROGBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 4); /// [ 4] .bss NOBITS 00000000 000044 000000 00 WA 0 0 4 LDSection* bss = builder.CreateELFHeader(*input, ".bss", llvm::ELF::SHT_NOBITS, llvm::ELF::SHF_ALLOC | llvm::ELF::SHF_WRITE, 4); builder.CreateBSS(*bss); /// [ 5] .ARM.attributes ARM_ATTRIBUTES 00000000 000044 000020 00 0 0 1 LDSection* attr = builder.CreateELFHeader(*input, ".ARM.attributes", llvm::ELF::SHT_ARM_ATTRIBUTES, 0x0, 1); SectionData* attr_data = builder.CreateSectionData(*attr); static uint8_t attr_content[] = { 0x41, 0x1f, 0x00, 0x00, 0x00, 0x61, 0x65, 0x61, 0x62, 0x69, 0x00, 0x01, 0x15, 0x00, 0x00, 0x00, 0x06, 0x02, 0x08, 0x01, 0x09, 0x01, 0x14, 0x01, 0x15, 0x01, 0x17, 0x03, 0x18, 0x01, 0x19, 0x01 }; Fragment* attr_frag = builder.CreateRegion(attr_content, 0x20); builder.AppendFragment(*attr_frag, *attr_data); /// Symbols /// 1: 00000000 0 FILE LOCAL DEFAULT ABS Output/gotplt.bc builder.AddSymbol(*input, "Output/gotplt.bc", ResolveInfo::File, ResolveInfo::Define, ResolveInfo::Local, 0); /// 2: 00000000 0 SECTION LOCAL DEFAULT 1 builder.AddSymbol(*input, ".text", ResolveInfo::Section, ResolveInfo::Define, ResolveInfo::Local, 0, 0x0, text); /// 3: 00000000 0 SECTION LOCAL DEFAULT 3 builder.AddSymbol(*input, ".data", ResolveInfo::Section, ResolveInfo::Define, ResolveInfo::Local, 0, 0x0, data); /// 4: 00000000 0 SECTION LOCAL DEFAULT 4 builder.AddSymbol(*input, ".bss", ResolveInfo::Section, ResolveInfo::Define, ResolveInfo::Local, 0, 0x0, bss); /// 5: 00000000 0 SECTION LOCAL DEFAULT 5 builder.AddSymbol(*input, ".ARM.attributes", ResolveInfo::Section, ResolveInfo::Define, ResolveInfo::Local, 0, 0x0, attr); /// 6: 00000000 16 FUNC GLOBAL DEFAULT 1 _Z1fv builder.AddSymbol(*input, "_Z1fv", ResolveInfo::Function, ResolveInfo::Define, ResolveInfo::Global, 16, 0x0, text); /// 7: 00000000 0 NOTYPE GLOBAL DEFAULT UND _Z1gv LDSymbol* z1gv = builder.AddSymbol(*input, "_Z1gv", ResolveInfo::NoType, ResolveInfo::Undefined, ResolveInfo::Global, 0); /// Relocations /// Offset Info Type Sym.Value Sym. Name /// 00000004 0000071b R_ARM_PLT32 00000000 _Z1gv builder.AddRelocation(*rel_text, llvm::ELF::R_ARM_PLT32, *z1gv, 0x4); if (linker.link(module, builder)) { linker.emit("libgotplt.so"); ///< -o libgotplt.so } Finalize(); }