//===-- RuntimeDyldELF.cpp - Run-time dynamic linker for MC-JIT -*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of ELF support for the MC-JIT runtime dynamic linker.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "dyld"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/IntervalMap.h"
#include "RuntimeDyldELF.h"
#include "llvm/Object/ObjectFile.h"
#include "llvm/Support/ELF.h"
#include "llvm/ADT/Triple.h"
using namespace llvm;
using namespace llvm::object;

namespace llvm {

namespace {

// FIXME: this function should probably not live here...
//
// Returns the name and address of an unrelocated symbol in an ELF section
void getSymbolInfo(symbol_iterator Sym, uint64_t &Addr, StringRef &Name) {
  //FIXME: error checking here required to catch corrupt ELF objects...
  error_code Err = Sym->getName(Name);

  uint64_t AddrInSection;
  Err = Sym->getAddress(AddrInSection);

  SectionRef empty_section;
  section_iterator Section(empty_section);
  Err = Sym->getSection(Section);

  StringRef SectionContents;
  Section->getContents(SectionContents);

  Addr = reinterpret_cast<uint64_t>(SectionContents.data()) + AddrInSection;
}

}

bool RuntimeDyldELF::loadObject(MemoryBuffer *InputBuffer) {
  if (!isCompatibleFormat(InputBuffer))
    return true;

  OwningPtr<ObjectFile> Obj(ObjectFile::createELFObjectFile(InputBuffer));

  Arch = Obj->getArch();

  // Map address in the Object file image to function names
  IntervalMap<uint64_t, StringRef>::Allocator A;
  IntervalMap<uint64_t, StringRef> FuncMap(A);

  // This is a bit of a hack.  The ObjectFile we've just loaded reports
  // section addresses as 0 and doesn't provide access to the section
  // offset (from which we could calculate the address.  Instead,
  // we're storing the address when it comes up in the ST_Debug case
  // below.
  //
  StringMap<uint64_t> DebugSymbolMap;

  symbol_iterator SymEnd = Obj->end_symbols();
  error_code Err;
  for (symbol_iterator Sym = Obj->begin_symbols();
       Sym != SymEnd; Sym.increment(Err)) {
    SymbolRef::Type Type;
    Sym->getType(Type);
    if (Type == SymbolRef::ST_Function) {
      StringRef Name;
      uint64_t Addr;
      getSymbolInfo(Sym, Addr, Name);

      uint64_t Size;
      Err = Sym->getSize(Size);

      uint8_t *Start;
      uint8_t *End;
      Start = reinterpret_cast<uint8_t*>(Addr);
      End   = reinterpret_cast<uint8_t*>(Addr + Size - 1);

      extractFunction(Name, Start, End);
      FuncMap.insert(Addr, Addr + Size - 1, Name);
    } else if (Type == SymbolRef::ST_Debug) {
      // This case helps us find section addresses
      StringRef Name;
      uint64_t Addr;
      getSymbolInfo(Sym, Addr, Name);
      DebugSymbolMap[Name] = Addr;
    }
  }

  // Iterate through the relocations for this object
  section_iterator SecEnd = Obj->end_sections();
  for (section_iterator Sec = Obj->begin_sections();
       Sec != SecEnd; Sec.increment(Err)) {
    StringRef SecName;
    uint64_t  SecAddr;
    Sec->getName(SecName);
    // Ignore sections that aren't in our map
    if (DebugSymbolMap.find(SecName) == DebugSymbolMap.end()) {
      continue;
    }
    SecAddr = DebugSymbolMap[SecName];
    relocation_iterator RelEnd = Sec->end_relocations();
    for (relocation_iterator Rel = Sec->begin_relocations();
         Rel != RelEnd; Rel.increment(Err)) {
      uint64_t RelOffset;
      uint64_t RelType;
      int64_t RelAddend;
      SymbolRef RelSym;
      StringRef SymName;
      uint64_t SymAddr;
      uint64_t SymOffset;

      Rel->getAddress(RelOffset);
      Rel->getType(RelType);
      Rel->getAdditionalInfo(RelAddend);
      Rel->getSymbol(RelSym);
      RelSym.getName(SymName);
      RelSym.getAddress(SymAddr);
      RelSym.getFileOffset(SymOffset);

      // If this relocation is inside a function, we want to store the
      // function name and a function-relative offset
      IntervalMap<uint64_t, StringRef>::iterator ContainingFunc
        = FuncMap.find(SecAddr + RelOffset);
      if (ContainingFunc.valid()) {
        // Re-base the relocation to make it relative to the target function
        RelOffset = (SecAddr + RelOffset) - ContainingFunc.start();
        Relocations[SymName].push_back(RelocationEntry(ContainingFunc.value(),
                                                       RelOffset,
                                                       RelType,
                                                       RelAddend,
                                                       true));
      } else {
        Relocations[SymName].push_back(RelocationEntry(SecName,
                                                       RelOffset,
                                                       RelType,
                                                       RelAddend,
                                                       false));
      }
    }
  }
  return false;
}

void RuntimeDyldELF::resolveRelocations() {
  // FIXME: deprecated. should be changed to use the by-section
  // allocation and relocation scheme.

  // Just iterate over the symbols in our symbol table and assign their
  // addresses.
  StringMap<SymbolLoc>::iterator i = SymbolTable.begin();
  StringMap<SymbolLoc>::iterator e = SymbolTable.end();
  for (;i != e; ++i) {
    assert (i->getValue().second == 0 && "non-zero offset in by-function sym!");
    reassignSymbolAddress(i->getKey(),
                          (uint8_t*)Sections[i->getValue().first].base());
  }
}

void RuntimeDyldELF::resolveX86_64Relocation(StringRef Name,
                                             uint8_t *Addr,
                                             const RelocationEntry &RE) {
  uint8_t *TargetAddr;
  if (RE.IsFunctionRelative) {
    StringMap<SymbolLoc>::const_iterator Loc = SymbolTable.find(RE.Target);
    assert(Loc != SymbolTable.end() && "Function for relocation not found");
    TargetAddr =
      reinterpret_cast<uint8_t*>(Sections[Loc->second.first].base()) +
      Loc->second.second + RE.Offset;
  } else {
    // FIXME: Get the address of the target section and add that to RE.Offset
    llvm_unreachable("Non-function relocation not implemented yet!");
  }

  switch (RE.Type) {
  default: llvm_unreachable("Relocation type not implemented yet!");
  case ELF::R_X86_64_64: {
    uint8_t **Target = reinterpret_cast<uint8_t**>(TargetAddr);
    *Target = Addr + RE.Addend;
    break;
  }
  case ELF::R_X86_64_32:
  case ELF::R_X86_64_32S: {
    uint64_t Value = reinterpret_cast<uint64_t>(Addr) + RE.Addend;
    // FIXME: Handle the possibility of this assertion failing
    assert((RE.Type == ELF::R_X86_64_32 && !(Value & 0xFFFFFFFF00000000ULL)) ||
           (RE.Type == ELF::R_X86_64_32S &&
            (Value & 0xFFFFFFFF00000000ULL) == 0xFFFFFFFF00000000ULL));
    uint32_t TruncatedAddr = (Value & 0xFFFFFFFF);
    uint32_t *Target = reinterpret_cast<uint32_t*>(TargetAddr);
    *Target = TruncatedAddr;
    break;
  }
  case ELF::R_X86_64_PC32: {
    uint32_t *Placeholder = reinterpret_cast<uint32_t*>(TargetAddr);
    uint64_t RealOffset = *Placeholder +
                           reinterpret_cast<uint64_t>(Addr) +
                           RE.Addend - reinterpret_cast<uint64_t>(TargetAddr);
    assert((RealOffset & 0xFFFFFFFF) == RealOffset);
    uint32_t TruncOffset = (RealOffset & 0xFFFFFFFF);
    *Placeholder = TruncOffset;
    break;
  }
  }
}

void RuntimeDyldELF::resolveX86Relocation(StringRef Name,
                                          uint8_t *Addr,
                                          const RelocationEntry &RE) {
  uint8_t *TargetAddr;
  if (RE.IsFunctionRelative) {
    StringMap<SymbolLoc>::const_iterator Loc = SymbolTable.find(RE.Target);
    assert(Loc != SymbolTable.end() && "Function for relocation not found");
    TargetAddr =
      reinterpret_cast<uint8_t*>(Sections[Loc->second.first].base()) +
      Loc->second.second + RE.Offset;
  } else {
    // FIXME: Get the address of the target section and add that to RE.Offset
    llvm_unreachable("Non-function relocation not implemented yet!");
  }

  switch (RE.Type) {
  case ELF::R_386_32: {
    uint8_t **Target = reinterpret_cast<uint8_t**>(TargetAddr);
    *Target = Addr + RE.Addend;
    break;
  }
  case ELF::R_386_PC32: {
    uint32_t *Placeholder = reinterpret_cast<uint32_t*>(TargetAddr);
    uint32_t RealOffset = *Placeholder + reinterpret_cast<uintptr_t>(Addr) +
                           RE.Addend - reinterpret_cast<uintptr_t>(TargetAddr);
    *Placeholder = RealOffset;
    break;
    }
    default:
      // There are other relocation types, but it appears these are the
      //  only ones currently used by the LLVM ELF object writer
      llvm_unreachable("Relocation type not implemented yet!");
  }
}

void RuntimeDyldELF::resolveArmRelocation(StringRef Name,
                                          uint8_t *Addr,
                                          const RelocationEntry &RE) {
}

void RuntimeDyldELF::resolveRelocation(StringRef Name,
                                       uint8_t *Addr,
                                       const RelocationEntry &RE) {
  switch (Arch) {
  case Triple::x86_64:
    resolveX86_64Relocation(Name, Addr, RE);
    break;
  case Triple::x86:
    resolveX86Relocation(Name, Addr, RE);
    break;
  case Triple::arm:
    resolveArmRelocation(Name, Addr, RE);
    break;
  default: llvm_unreachable("Unsupported CPU type!");
  }
}

void RuntimeDyldELF::reassignSymbolAddress(StringRef Name, uint8_t *Addr) {
  // FIXME: deprecated. switch to reassignSectionAddress() instead.
  //
  // Actually moving the symbol address requires by-section mapping.
  assert(Sections[SymbolTable.lookup(Name).first].base() == (void*)Addr &&
         "Unable to relocate section in by-function JIT allocation model!");

  RelocationList &Relocs = Relocations[Name];
  for (unsigned i = 0, e = Relocs.size(); i != e; ++i) {
    RelocationEntry &RE = Relocs[i];
    resolveRelocation(Name, Addr, RE);
  }
}

// Assign an address to a symbol name and resolve all the relocations
// associated with it.
void RuntimeDyldELF::reassignSectionAddress(unsigned SectionID, uint64_t Addr) {
  // The address to use for relocation resolution is not
  // the address of the local section buffer. We must be doing
  // a remote execution environment of some sort. Re-apply any
  // relocations referencing this section with the given address.
  //
  // Addr is a uint64_t because we can't assume the pointer width
  // of the target is the same as that of the host. Just use a generic
  // "big enough" type.
  assert(0);
}

bool RuntimeDyldELF::isCompatibleFormat(const MemoryBuffer *InputBuffer) const {
  StringRef Magic = InputBuffer->getBuffer().slice(0, ELF::EI_NIDENT);
  return (memcmp(Magic.data(), ELF::ElfMagic, strlen(ELF::ElfMagic))) == 0;
}
} // namespace llvm
