C++程序  |  476行  |  15.58 KB

//===- 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/LinkerScript.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");
  LinkerScript script;
  Module module("test", script);
  config.setCodeGenType(LinkerConfig::DynObj);

  Linker linker;
  linker.emulate(script, config);

  IRBuilder builder(module, config);
  // create inputs here
  //   builder.CreateInput("./test.o");

  if (linker.link(module, builder))
    linker.emit(module, "./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;
  LinkerScript script;

  ///< --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");
  script.directories().insert(search_dir);

  /// To configure linker before setting options. Linker::config sets up
  /// default target-dependent configuration to LinkerConfig.
  linker.emulate(script, config);

  config.setCodeGenType(LinkerConfig::DynObj);  ///< --shared
  config.options().setSOName("libplasma.so");   ///< --soname=libplasma.so
  config.options().setBsymbolic();              ///< -Bsymbolic

  Module module("libplasma.so", script);
  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(module, "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");

  LinkerScript script1;
  /// -L=${TOPDIR}/test/libs/ARM/Android/android-14
  Path search_dir(TOPDIR);
  search_dir.append("test/libs/ARM/Android/android-14");
  script1.directories().insert(search_dir);

  /// To configure linker before setting options. Linker::config sets up
  /// default target-dependent configuration to LinkerConfig.
  linker.emulate(script1, 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", script1);
  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(module1, "libplasma.once.so"); ///< -o libplasma.so
  }

  Finalize();

  linker.reset();

  Initialize();

  ///< --mtriple="armv7-none-linux-gnueabi"
  LinkerConfig config2("armv7-none-linux-gnueabi");

  LinkerScript script2;
  /// -L=${TOPDIR}/test/libs/ARM/Android/android-14
  script2.directories().insert(search_dir);

  /// To configure linker before setting options. Linker::config sets up
  /// default target-dependent configuration to LinkerConfig.
  linker.emulate(script2, config2);

  config2.setCodeGenType(LinkerConfig::DynObj);  ///< --shared
  config2.options().setSOName("libplasma.twice.so");   ///< --soname=libplasma.twice.exe
  config2.options().setBsymbolic();              ///< -Bsymbolic

  Module module2("libplasma.so", script2);
  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(module2, "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");

  LinkerScript script1;
  /// -L=${TOPDIR}/test/libs/ARM/Android/android-14
  Path search_dir(TOPDIR);
  search_dir.append("test/libs/ARM/Android/android-14");
  script1.directories().insert(search_dir);

  /// To configure linker before setting options. Linker::config sets up
  /// default target-dependent configuration to LinkerConfig.
  linker.emulate(script1, 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", script1);
  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(module1, "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");

  LinkerScript script2;
  /// -L=${TOPDIR}/test/libs/ARM/Android/android-14
  script2.directories().insert(search_dir);

  /// To configure linker before setting options. Linker::config sets up
  /// default target-dependent configuration to LinkerConfig.
  linker.emulate(script2, config2);

  config2.setCodeGenType(LinkerConfig::DynObj);  ///< --shared
  config2.options().setSOName("libplasma.twice.so");   ///< --soname=libplasma.twice.exe
  config2.options().setBsymbolic();              ///< -Bsymbolic

  Module module2("libplasma.so", script2);
  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(module2, "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");
  LinkerScript script;

  /// To configure linker before setting options. Linker::config sets up
  /// default target-dependent configuration to LinkerConfig.
  linker.emulate(script, config);

  config.setCodeGenType(LinkerConfig::DynObj);  ///< --shared
  config.options().setSOName("libgotplt.so");   ///< --soname=libgotplt.so

  Module module(script);
  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(module, "libgotplt.so"); ///< -o libgotplt.so
  }

  Finalize();
}