/*
 * Copyright 2011, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ELF_SYMBOL_H
#define ELF_SYMBOL_H

#include "ELFTypes.h"
#include "ELF.h"

#include <llvm/ADT/OwningPtr.h>

#include <string>
#include <algorithm>

#include <stdint.h>
#include <stdlib.h>

class ELFSymbolHelperMixin {
protected:
  static char const *getTypeStr(uint8_t);
  static char const *getBindingAttributeStr(uint8_t);
  static char const *getVisibilityStr(uint8_t);
};

template <unsigned Bitwidth>
class ELFSymbol_CRTP : private ELFSymbolHelperMixin {
public:
  ELF_TYPE_INTRO_TO_TEMPLATE_SCOPE(Bitwidth);

protected:
  ELFObject<Bitwidth> const *owner;

  size_t index;

  word_t st_name;
  byte_t st_info;
  byte_t st_other;
  half_t st_shndx;
  addr_t st_value;
  symsize_t st_size;

  mutable void *my_addr;

protected:
  ELFSymbol_CRTP() { my_addr = 0; }

  ~ELFSymbol_CRTP() {
#if 0
    if (my_addr != 0 &&
        getType() == STT_OBJECT &&
        getSectionIndex() == SHN_COMMON) {
      std::free(my_addr);
    }
#endif
  }

public:
  size_t getIndex() const {
    return index;
  }

  word_t getNameIndex() const {
    return st_name;
  }

  char const *getName() const;

// I don't want to include elf.h in .h file, so define those macro by ourself.
#define ELF_ST_BIND(i)   ((i)>>4)
#define ELF_ST_TYPE(i)   ((i)&0xf)
#define ELF_ST_INFO(b,t) (((b)<<4)+((t)&0xf))
  byte_t getType() const {
    return ELF_ST_TYPE(st_info);
  }

  byte_t getBindingAttribute() const {
    return ELF_ST_BIND(st_info);
  }
#undef ELF_ST_BIND
#undef ELF_ST_TYPE
#undef ELF_ST_INFO

#define ELF_ST_VISIBILITY(o) ((o)&0x3)
  byte_t getVisibility() const {
    return ELF_ST_VISIBILITY(st_other);
  }
#undef ELF_ST_VISIBILITY

  half_t getSectionIndex() const {
    return st_shndx;
  }

  addr_t getValue() const {
    return st_value;
  }

  symsize_t getSize() const {
    return st_size;
  }

  void *getAddress(int machine, bool autoAlloc = true) const;

  void setAddress(void *addr) {
    my_addr = addr;
  }

  bool isValid() const {
    // FIXME: Should check the correctness of the section header.
    return true;
  }

  bool isConcreteFunc() const {
    return getType() == STT_FUNC;
  }

  bool isExternFunc() const {
    return getType() == STT_NOTYPE;
  }

  template <typename Archiver>
  static ELFSymbolTy *
  read(Archiver &AR, ELFObject<Bitwidth> const *owner, size_t index = 0);

  void print(bool shouldPrintHeader = false) const;

private:
  ELFSymbolTy *concrete() {
    return static_cast<ELFSymbolTy *>(this);
  }

  ELFSymbolTy const *concrete() const {
    return static_cast<ELFSymbolTy const *>(this);
  }
};

template <>
class ELFSymbol<32> : public ELFSymbol_CRTP<32> {
  friend class ELFSymbol_CRTP<32>;

private:
  ELFSymbol() {
  }

  template <typename Archiver>
  bool serialize(Archiver &AR) {
    AR.prologue(TypeTraits<ELFSymbol>::size);

    AR & st_name;
    AR & st_value;
    AR & st_size;
    AR & st_info;
    AR & st_other;
    AR & st_shndx;

    AR.epilogue(TypeTraits<ELFSymbol>::size);
    return AR;
  }
};

template <>
class ELFSymbol<64> : public ELFSymbol_CRTP<64> {
  friend class ELFSymbol_CRTP<64>;

private:
  ELFSymbol() {
  }

  template <typename Archiver>
  bool serialize(Archiver &AR) {
    AR.prologue(TypeTraits<ELFSymbol>::size);

    AR & st_name;
    AR & st_info;
    AR & st_other;
    AR & st_shndx;
    AR & st_value;
    AR & st_size;

    AR.epilogue(TypeTraits<ELFSymbol>::size);
    return AR;
  }
};

#include "impl/ELFSymbol.hxx"

#endif // ELF_SYMBOL_H