text: ////////////////////////////////////////////////////////////////////////////////
  //
  // The University of Illinois/NCSA
  // Open Source License (NCSA)
  //
  // Copyright (c) 2014-2016, Advanced Micro Devices, Inc. All rights reserved.
  //
  // Developed by:
  //
  //                 AMD Research and AMD HSA Software Development
  //
  //                 Advanced Micro Devices, Inc.
  //
  //                 www.amd.com
  //
  // Permission is hereby granted, free of charge, to any person obtaining a copy
  // of this software and associated documentation files (the "Software"), to
  // deal with the Software without restriction, including without limitation
  // the rights to use, copy, modify, merge, publish, distribute, sublicense,
  // and/or sell copies of the Software, and to permit persons to whom the
  // Software is furnished to do so, subject to the following conditions:
  //
  //  - Redistributions of source code must retain the above copyright notice,
  //    this list of conditions and the following disclaimers.
  //  - Redistributions in binary form must reproduce the above copyright
  //    notice, this list of conditions and the following disclaimers in
  //    the documentation and/or other materials provided with the distribution.
  //  - Neither the names of Advanced Micro Devices, Inc,
  //    nor the names of its contributors may be used to endorse or promote
  //    products derived from this Software without specific prior written
  //    permission.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  // THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  // DEALINGS WITH THE SOFTWARE.
  //
  ////////////////////////////////////////////////////////////////////////////////
  
  #include "amd_elf_image.hpp"
  #include "amd_hsa_code_util.hpp"
  #include <gelf.h>
  #include <errno.h>
  #include <cstring>
  #include <cerrno>
  #include <fstream>
  #include <memory>
  #include <cassert>
  #include <cstdlib>
  #include <algorithm>
  #ifdef _WIN32
  #include <Windows.h>
  #define alignof __alignof
  #endif // _WIN32
  #include <libelf.h>
  
  #ifndef _WIN32
  #define _open open
  #define _close close
  #define _tempnam tempnam
  #include <fcntl.h>
  #include <unistd.h>
  #endif
  
  #if defined(USE_MEMFILE)
  
  #include "memfile.h"
  #define OpenTemp(f)           mem_open(NULL, 0, 0)
  #define CloseTemp(f)          mem_close(f)
  #define _read(f, b, l)        mem_read((f), (b), (l))
  #define _write(f, b, l)       mem_write((f), (b), (l))
  #define _lseek(f, l, w)       mem_lseek((f), (l), (w))
  #define _ftruncate(f, l)      mem_ftruncate((f), (size_t)(l))
  #define sendfile(o, i, p, s)  mem_sendfile((o), (i), (p), (s))
  
  #else // USE_MEMFILE
  
  #define OpenTemp(f) amd::hsa::OpenTempFile(f);
  #define CloseTemp(f) amd::hsa::CloseTempFile(f);
  
  #ifndef _WIN32
  #define _read read
  #define _write write
  #define _lseek lseek
  #define _ftruncate ftruncate
  #include <sys/sendfile.h>
  #else
  #define _ftruncate _chsize
  #endif // !_WIN32
  
  #endif // !USE_MEMFILE
  
  #if !defined(BSD_LIBELF)
    #define elf_setshstrndx elfx_update_shstrndx
  #endif
  
  #define NOTE_RECORD_ALIGNMENT 4
  
  using amd::hsa::alignUp;
  
  namespace amd {
    namespace elf {
  
      class FileImage {
      public:
        FileImage();
        ~FileImage();
        bool create();
        bool readFrom(const std::string& filename);
        bool copyFrom(const void* data, size_t size);
        bool writeTo(const std::string& filename);
        bool copyTo(void** buffer, size_t* size = 0);
        bool copyTo(void* buffer, size_t size);
        size_t getSize();
  
        std::string output() { return out.str(); }
  
        int fd() { return d; }
  
      private:
        int d;
        std::ostringstream out;
  
        bool error(const char* msg);
        bool perror(const char *msg);
        std::string werror();
      };
  
      FileImage::FileImage()
        : d(-1)
      {
      }
  
      FileImage::~FileImage()
      {
        if (d != -1) { CloseTemp(d); }
      }
  
      bool FileImage::error(const char* msg)
      {
        out << "Error: " << msg << std::endl;
        return false;
      }
  
      bool FileImage::perror(const char* msg)
      {
        out << "Error: " << msg << ": " << strerror(errno) << std::endl;
        return false;
      }
  
  #ifdef _WIN32
      std::string FileImage::werror()
      {
        LPVOID lpMsgBuf;
        DWORD dw = GetLastError();
  
        FormatMessage(
          FORMAT_MESSAGE_ALLOCATE_BUFFER |
          FORMAT_MESSAGE_FROM_SYSTEM |
          FORMAT_MESSAGE_IGNORE_INSERTS,
          NULL,
          dw,
          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
          (LPTSTR)&lpMsgBuf,
          0, NULL);
        std::string result((LPTSTR)lpMsgBuf);
        LocalFree(lpMsgBuf);
        return result;
      }
  #endif // _WIN32
  
      bool FileImage::create()
      {
        d = OpenTemp("amdelf");
        if (d == -1) { return error("Failed to open temporary file for elf image"); }
        return true;
      }
  
      bool FileImage::readFrom(const std::string& filename)
      {
  #ifdef _WIN32
        std::unique_ptr<char> buffer(new char[32 * 1024 * 1024]);
        HANDLE in = CreateFile(filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        if (in == INVALID_HANDLE_VALUE) { out << "Failed to open " << filename << ": " << werror() << std::endl; return false; }
        DWORD read;
        unsigned write;
        int written;
        do {
          if (!ReadFile(in, buffer.get(), sizeof(buffer), &read, NULL)) {
            out << "Failed to read " << filename << ": " << werror() << std::endl;
            CloseHandle(in);
            return false;
          }
          if (read > 0) {
            write = read;
            do {
              written = _write(d, buffer.get(), write);
              if (written < 0) {
                out << "Failed to write image file: " << werror() << std::endl;
                CloseHandle(in);
              }
              write -= written;
            } while (write > 0);
          }
        } while (read > 0);
        if (_lseek(d, 0L, SEEK_SET) < 0) { return perror("lseek(0) failed"); }
        CloseHandle(in);
        return true;
  #else // _WIN32
        int in = _open(filename.c_str(), O_RDONLY);
        if (in < 0) { return perror("open failed"); }
        if (_lseek(in, 0L, SEEK_END) < 0) { return perror("lseek failed"); }
        off_t size;
        if ((size = _lseek(in, 0L, SEEK_CUR)) < 0) { return perror("lseek(2) failed"); }
        if (_lseek(in, 0L, SEEK_SET) < 0) { return perror("lseek(3) failed"); }
        if (_lseek(d, 0L, SEEK_SET) < 0) { return perror("lseek(3) failed"); }
        ssize_t written;
        do {
          written = sendfile(d, in, NULL, size);
          if (written < 0) {
            _close(in);
            return perror("sendfile failed");
          }
          size -= written;
        } while (size > 0);
        _close(in);
        if (_lseek(d, 0L, SEEK_SET) < 0) { return perror("lseek(0) failed"); }
        return true;
  #endif // _WIN32
      }
  
      bool FileImage::copyFrom(const void* data, size_t size)
      {
        assert(d != -1);
        if (_lseek(d, 0L, SEEK_SET) < 0) { return perror("lseek failed"); }
        if (_ftruncate(d, 0) < 0) { return perror("ftruncate failed"); }
        int written, offset = 0;
        while (size > 0) {
          written = _write(d, (const char*) data + offset, size);
          if (written < 0) {
            return perror("write failed");
          }
          size -= written;
          offset += written;
        }
        if (_lseek(d, 0L, SEEK_SET) < 0) { return perror("lseek failed"); }
        return true;
      }
  
      size_t FileImage::getSize()
      {
        assert(d != -1);
        if (_lseek(d, 0L, SEEK_END) < 0) { return perror("lseek failed"); }
        long seek = 0;
        if ((seek = _lseek(d, 0L, SEEK_CUR)) < 0) { return perror("lseek(2) failed"); }
        if (_lseek(d, 0L, SEEK_SET) < 0) { return perror("lseek(3) failed"); }
        return seek;
      }
  
      bool FileImage::copyTo(void** buffer, size_t* size)
      {
        size_t size1 = getSize();
        void* buffer1 = malloc(size1);
        if (_read(d, buffer1, size1) < 0) { free(buffer1); return perror("read failed"); }
        *buffer = buffer1;
        if (size) { *size = size1; }
        return true;
      }
  
      bool FileImage::copyTo(void* buffer, size_t size)
      {
        size_t size1 = getSize();
        if (size < size1) { return error("Buffer size is not enough"); }
        if (_read(d, buffer, size1) < 0) { return perror("read failed"); }
        return true;
      }
  
      bool FileImage::writeTo(const std::string& filename)
      {
        bool res = false;
        size_t size = 0;
        void *buffer = nullptr;
        if (copyTo(&buffer, &size)) {
          res = true;
          std::ofstream out(filename.c_str(), std::ios::binary);
          out.write((char*)buffer, size);
        }
        free(buffer);
        return res;
      }
  
      class Buffer {
      public:
        typedef unsigned char byte_type;
        typedef size_t size_type;
  
        Buffer();
        Buffer(const byte_type *src, size_type size, size_type align = 0);
        virtual ~Buffer();
  
        const byte_type* raw() const
          { return this->isConst() ? ptr_ : data_.data(); }
        size_type align() const
          { return align_; }
        size_type size() const
          { return this->isConst() ? size_ : data_.size(); }
        bool isConst() const
          { return 0 != size_; }
        bool isEmpty()
          { return size() == 0; }
        bool hasRaw(const byte_type *src) const
          { return (src >= this->raw()) && (src < this->raw() + this->size()); }
        template<typename T>
        bool has(const T *src) const
          { return this->hasRaw((const byte_type*)src); }
        bool has(size_type offset) const
          { return offset < this->size(); }
  
        template<typename T>
        size_type getOffset(const T *src) const
          { return this->getRawOffset((const byte_type*)src); }
        template<typename T>
        T get(size_type offset) const
          { return (T)this->getRaw(offset); }
        size_type addString(const std::string &str, size_type align = 0);
        size_type addStringLength(const std::string &str, size_type align = 0);
        size_type nextOffset(size_type align) const { return alignUp(this->size(), align); }
        template<typename T>
        size_type add(const T *src, size_type size, size_type align)
          { return this->addRaw((const byte_type*)src, size, align); }
        template<typename T>
        size_type add(const T &src, size_type align = 0)
          { return this->addRaw((const byte_type*)&src, sizeof(T), align == 0 ? alignof(T) : align); }
        size_type align(size_type align);
  
        template<typename T>
        size_type reserve()
        {
          Buffer::size_type offset = this->align(alignof(T));
          data_.insert(data_.end(), sizeof(T), 0x0);
          return offset;
        }
  
      private:
        size_type getRawOffset(const byte_type *src) const;
        const byte_type* getRaw(size_type offset) const;
        size_type addRaw(const byte_type *src, size_type size, size_type align);
  
        std::vector<byte_type> data_;
        const byte_type *ptr_;
        size_type size_;
        size_type align_;
      };
  
      Buffer::Buffer()
        : ptr_(nullptr)
        , size_(0)
        , align_(0)
      {
      }
  
      Buffer::Buffer(const Buffer::byte_type *src, Buffer::size_type size, Buffer::size_type align)
        : ptr_(src)
        , size_(size)
        , align_(align)
      {
      }
  
      Buffer::~Buffer()
      {
      }
  
      Buffer::size_type Buffer::getRawOffset(const Buffer::byte_type *src) const
      {
        assert(this->has(src));
        return src - this->raw();
      }
  
      const Buffer::byte_type* Buffer::getRaw(Buffer::size_type offset) const
      {
        assert(this->has(offset));
        return this->raw() + offset;
      }
  
      Buffer::size_type Buffer::addRaw(const Buffer::byte_type *src, Buffer::size_type size, Buffer::size_type align)
      {
        assert(!this->isConst());
        assert(nullptr != src);
        assert(0 != size);
        assert(0 != align);
        Buffer::size_type offset = this->align(align);
        data_.insert(data_.end(), src, src + size);
        return offset;
      }
  
      Buffer::size_type Buffer::addString(const std::string &str, size_type align)
      {
        return this->add(str.c_str(), str.length() + 1, align == 0 ? alignof(char) : align);
      }
  
      Buffer::size_type Buffer::addStringLength(const std::string &str, size_type align)
      {
        return this->add((uint32_t)(str.length() + 1), align == 0 ? alignof(uint32_t) : align);
      }
  
      Buffer::size_type Buffer::align(Buffer::size_type align)
      {
        assert(!this->isConst());
        assert(0 != align);
        Buffer::size_type offset = alignUp(this->size(), align);
        align_ = (std::max)(align_, align);
        data_.insert(data_.end(), offset - this->size(), 0x0);
        return offset;
      }
  
      class GElfImage;
      class GElfSegment;
  
      class GElfSection : public virtual Section {
      public:
        GElfSection(GElfImage* elf);
  
        bool push(const char* name, uint32_t shtype, uint64_t shflags, uint16_t shlink, uint32_t info, uint32_t align, uint64_t entsize = 0);
        bool pull0();
        bool pull(uint16_t ndx);
        virtual bool pullData() { return true; }
        bool push();
        uint16_t getSectionIndex() const override;
        uint32_t type() const override { return hdr.sh_type; }
        std::string Name() const override;
        uint64_t offset() const override { return hdr.sh_offset; }
        uint64_t addr() const override { return hdr.sh_addr; }
        bool updateAddr(uint64_t addr) override;
        uint64_t addralign() const override { return data0.size() == 0 ? data.align() : data0.align(); }
        uint64_t flags() const override { return hdr.sh_flags; }
        uint64_t size() const override { return data0.size() == 0 ? data.size() : data0.size(); }
        uint64_t nextDataOffset(uint64_t align) const override;
        uint64_t addData(const void *src, uint64_t size, uint64_t align) override;
        bool getData(uint64_t offset, void* dest, uint64_t size) override;
        bool hasRelocationSection() const override { return reloc_sec != 0; }
        RelocationSection* relocationSection(SymbolTable* symtab = 0) override;
        Segment* segment() override { return seg; }
        RelocationSection* asRelocationSection() override { return 0; }
        bool setMemSize(uint64_t s) override { memsize_ = s; return true; }
        uint64_t memSize() const override { return memsize_ ? memsize_ : size(); }
        bool setAlign(uint64_t a) override { align_ = a; return true; }
        uint64_t memAlign() const override { return align_ ? align_ : addralign(); }
  
      protected:
        GElfImage* elf;
        Segment* seg;
        GElf_Shdr hdr;
        Buffer data0, data;
        uint64_t memsize_;
        uint64_t align_;
        RelocationSection *reloc_sec;
  
        size_t ndxscn;
  
        friend class GElfSymbol;
        friend class GElfSegment;
        friend class GElfImage;
      };
  
      class GElfSegment : public Segment {
      public:
        GElfSegment(GElfImage* elf, uint16_t index);
        GElfSegment(GElfImage* elf, uint16_t index, uint32_t type, uint32_t flags, uint64_t paddr = 0);
        bool push(uint64_t vaddr);
        bool pull();
        uint64_t type() const override { return phdr.p_type; }
        uint64_t memSize() const override { return phdr.p_memsz; }
        uint64_t align() const override { return phdr.p_align; }
        uint64_t imageSize() const override { return phdr.p_filesz; }
        uint64_t vaddr() const override { return phdr.p_vaddr; }
        uint64_t flags() const override { return phdr.p_flags; }
        uint64_t offset() const override { return phdr.p_offset; }
        const char* data() const override;
        uint16_t getSegmentIndex() override;
        bool updateAddSection(Section *section) override;
  
      private:
        GElfImage* elf;
        uint16_t index;
        GElf_Phdr phdr;
        std::vector<Section*> sections;
      };
  
      class GElfStringTable : public GElfSection, public StringTable {
      public:
        GElfStringTable(GElfImage* elf);
        bool push(const char* name, uint32_t shtype, uint64_t shflags);
        bool pullData() override;
        const char* addString(const std::string& s) override;
        size_t addString1(const std::string& s) override;
        const char* getString(size_t ndx) override;
        size_t getStringIndex(const char* name) override;
  
        uint16_t getSectionIndex() const override { return GElfSection::getSectionIndex(); }
        uint32_t type() const override { return GElfSection::type(); }
        std::string Name() const override { return GElfSection::Name(); }
        uint64_t addr() const override { return GElfSection::addr(); }
        uint64_t offset() const override { return GElfSection::offset(); }
        bool updateAddr(uint64_t addr) override { return GElfSection::updateAddr(addr); }
        uint64_t addralign() const override { return GElfSection::addralign(); }
        uint64_t flags() const override { return GElfSection::flags(); }
        uint64_t size() const override { return GElfSection::size(); }
        Segment* segment() override { return GElfSection::segment(); }
        uint64_t nextDataOffset(uint64_t align) const override { return GElfSection::nextDataOffset(align); }
        uint64_t addData(const void *src, uint64_t size, uint64_t align) override { return GElfSection::addData(src, size, align); }
        bool getData(uint64_t offset, void* dest, uint64_t size) override { return GElfSection::getData(offset, dest, size); }
        bool hasRelocationSection() const override { return GElfSection::hasRelocationSection(); }
        RelocationSection* relocationSection(SymbolTable* symtab) override { return GElfSection::relocationSection(); }
        RelocationSection* asRelocationSection() override { return 0; }
        uint64_t memSize() const override { return GElfSection::memSize(); }
        bool setMemSize(uint64_t s) override { return GElfSection::setMemSize(s); }
        uint64_t memAlign() const override { return GElfSection::memAlign(); }
        bool setAlign(uint64_t a) override { return GElfSection::setAlign(a); }
      };
  
      class GElfSymbolTable;
  
      class GElfSymbol : public Symbol {
      public:
        GElfSymbol(GElfSymbolTable* symtab, Buffer &data, size_t index);
  
        bool push(const std::string& name, uint64_t value, uint64_t size, unsigned char type, unsigned char binding, uint16_t shndx, unsigned char other);
  
        uint32_t index() override { return eindex / sizeof(GElf_Rela); }
        uint32_t type() override { return GELF_ST_TYPE(Sym()->st_info); }
        uint32_t binding() override { return GELF_ST_BIND(Sym()->st_info); }
        uint64_t size() override { return Sym()->st_size; }
        uint64_t value() override { return Sym()->st_value; }
        unsigned char other() override { return Sym()->st_other; }
        std::string name() override;
        Section* section() override;
  
        void setValue(uint64_t value) override { Sym()->st_value = value; }
        void setSize(uint64_t size) override { Sym()->st_size = size; }
  
      private:
        GElf_Sym* Sym() { return edata.get<GElf_Sym*>(eindex); }
        GElfSymbolTable* symtab;
        Buffer &edata;
        size_t eindex;
        friend class GElfSymbolTable;
      };
  
      class GElfSymbolTable : public GElfSection, public SymbolTable {
      private:
        Symbol* addSymbolInternal(Section* section, const std::string& name, uint64_t value, uint64_t size, unsigned char type, unsigned char binding, unsigned char other = 0);
  
        GElfStringTable* strtab;
        std::vector<std::unique_ptr<GElfSymbol>> symbols;
        friend class GElfSymbol;
  
      public:
        GElfSymbolTable(GElfImage* elf);
        bool push(const char* name, GElfStringTable* strtab);
        bool pullData() override;
        uint16_t getSectionIndex() const override { return GElfSection::getSectionIndex(); }
        uint32_t type() const override { return GElfSection::type(); }
        std::string Name() const override { return GElfSection::Name(); }
        uint64_t offset() const override { return GElfSection::offset(); }
        uint64_t addr() const override { return GElfSection::addr(); }
        bool updateAddr(uint64_t addr) override { return GElfSection::updateAddr(addr); }
        uint64_t addralign() const override { return GElfSection::addralign(); }
        uint64_t flags() const override { return GElfSection::flags(); }
        uint64_t size() const override { return GElfSection::size(); }
        Segment* segment() override { return GElfSection::segment(); }
        uint64_t nextDataOffset(uint64_t align) const override { return GElfSection::nextDataOffset(align); }
        uint64_t addData(const void *src, uint64_t size, uint64_t align) override { return GElfSection::addData(src, size, align); }
        bool getData(uint64_t offset, void* dest, uint64_t size) override { return GElfSection::getData(offset, dest, size); }
        bool hasRelocationSection() const override { return GElfSection::hasRelocationSection(); }
        RelocationSection* relocationSection(SymbolTable* symtab) override { return GElfSection::relocationSection(); }
        Symbol* addSymbol(Section* section, const std::string& name, uint64_t value, uint64_t size, unsigned char type, unsigned char binding, unsigned char other = 0) override;
        size_t symbolCount() override;
        Symbol* symbol(size_t i) override;
        RelocationSection* asRelocationSection() override { return 0; }
        uint64_t memSize() const override { return GElfSection::memSize(); }
        bool setMemSize(uint64_t s) override { return GElfSection::setMemSize(s); }
        uint64_t memAlign() const override { return GElfSection::memAlign(); }
        bool setAlign(uint64_t a) override { return GElfSection::setAlign(a); }
      };
  
      class GElfNoteSection : public GElfSection, public NoteSection {
      public:
        GElfNoteSection(GElfImage* elf);
        bool push(const std::string& name);
        uint16_t getSectionIndex() const override { return GElfSection::getSectionIndex(); }
        uint32_t type() const override { return GElfSection::type(); }
        std::string Name() const override { return GElfSection::Name(); }
        uint64_t addr() const override { return GElfSection::addr(); }
        bool updateAddr(uint64_t addr) override { return GElfSection::updateAddr(addr); }
        uint64_t offset() const override { return GElfSection::offset(); }
        uint64_t addralign() const override { return GElfSection::addralign(); }
        uint64_t flags() const override { return GElfSection::flags(); }
        uint64_t size() const override { return GElfSection::size(); }
        Segment* segment() override { return GElfSection::segment(); }
        uint64_t nextDataOffset(uint64_t align) const override { return GElfSection::nextDataOffset(align); }
        uint64_t addData(const void *src, uint64_t size, uint64_t align) override { return GElfSection::addData(src, size, align); }
        bool getData(uint64_t offset, void* dest, uint64_t size) override { return GElfSection::getData(offset, dest, size); }
        bool hasRelocationSection() const override { return GElfSection::hasRelocationSection(); }
        RelocationSection* relocationSection(SymbolTable* symtab) override { return GElfSection::relocationSection(); }
        bool addNote(const std::string& name, uint32_t type, const void* desc, uint32_t desc_size) override;
        bool getNote(const std::string& name, uint32_t type, void** desc, uint32_t* desc_size) override;
        RelocationSection* asRelocationSection() override { return 0; }
        uint64_t memSize() const override { return GElfSection::memSize(); }
        bool setMemSize(uint64_t s) override { return GElfSection::setMemSize(s); }
        uint64_t memAlign() const override { return GElfSection::memAlign(); }
        bool setAlign(uint64_t a) override { return GElfSection::setAlign(a); }
      };
  
      class GElfRelocationSection;
  
      class GElfRelocation : public Relocation {
      private:
        GElf_Rela *Rela() { return edata.get<GElf_Rela*>(eindex); }
  
        GElfRelocationSection* rsection;
        Buffer &edata;
        size_t eindex;
  
      public:
        GElfRelocation(GElfRelocationSection* rsection_, Buffer &edata_, size_t eindex_)
          : rsection(rsection_),
            edata(edata_), eindex(eindex_)
        {
        }
  
        bool push(uint32_t type, Symbol* symbol, uint64_t offset, int64_t addend);
  
        RelocationSection* section() override;
        uint32_t type() override { return GELF_R_TYPE(Rela()->r_info); }
        uint32_t symbolIndex() override { return GELF_R_SYM(Rela()->r_info); }
        Symbol* symbol() override;
        uint64_t offset() override { return Rela()->r_offset; }
        int64_t addend() override { return Rela()->r_addend; }
      };
  
      class GElfRelocationSection : public GElfSection, public RelocationSection {
      private:
        Section* section;
        GElfSymbolTable* symtab;
        std::vector<std::unique_ptr<GElfRelocation>> relocations;
  
      public:
        GElfRelocationSection(GElfImage* elf, Section* targetSection = 0, GElfSymbolTable* symtab_ = 0);
        bool push(const std::string& name);
        bool pullData() override;
        uint16_t getSectionIndex() const override { return GElfSection::getSectionIndex(); }
        uint32_t type() const override { return GElfSection::type(); }
        std::string Name() const override { return GElfSection::Name(); }
        uint64_t addr() const override { return GElfSection::addr(); }
        uint64_t offset() const override { return GElfSection::offset(); }
        bool updateAddr(uint64_t addr) override { return GElfSection::updateAddr(addr); }
        uint64_t addralign() const override { return GElfSection::addralign(); }
        uint64_t flags() const override { return GElfSection::flags(); }
        uint64_t size() const override { return GElfSection::size(); }
        Segment* segment() override { return GElfSection::segment(); }
        uint64_t nextDataOffset(uint64_t align) const override { return GElfSection::nextDataOffset(align); }
        uint64_t addData(const void *src, uint64_t size, uint64_t align) override { return GElfSection::addData(src, size, align); }
        bool getData(uint64_t offset, void* dest, uint64_t size) override { return GElfSection::getData(offset, dest, size); }
        bool hasRelocationSection() const override { return GElfSection::hasRelocationSection(); }
        RelocationSection* relocationSection(SymbolTable* symtab) override { return GElfSection::relocationSection(); }
        RelocationSection* asRelocationSection() override { return this; }
  
        size_t relocationCount() const override { return relocations.size(); }
        Relocation* relocation(size_t i) override { return relocations[i].get(); }
        Relocation* addRelocation(uint32_t type, Symbol* symbol, uint64_t offset, int64_t addend) override;
        Section* targetSection() override { return section; }
        uint64_t memSize() const override { return GElfSection::memSize(); }
        bool setMemSize(uint64_t s) override { return GElfSection::setMemSize(s); }
        uint64_t memAlign() const override { return GElfSection::memAlign(); }
        bool setAlign(uint64_t a) override { return GElfSection::setAlign(a); }
        friend class GElfRelocation;
      };
  
      class GElfImage : public Image {
      public:
        GElfImage(int elfclass);
        ~GElfImage();
        bool initNew(uint16_t machine, uint16_t type, uint8_t os_abi = 0, uint8_t abi_version = 0, uint32_t e_flags = 0) override;
        bool loadFromFile(const std::string& filename) override;
        bool saveToFile(const std::string& filename) override;
        bool initFromBuffer(const void* buffer, size_t size) override;
        bool initAsBuffer(const void* buffer, size_t size) override;
        bool close();
        bool writeTo(const std::string& filename) override;
        bool copyToBuffer(void** buf, size_t* size = 0) override;
        bool copyToBuffer(void* buf, size_t size) override;
  
        const char* data() override { assert(buffer); return buffer; }
        uint64_t size() override;
  
        bool push();
  
        bool Freeze() override;
        bool Validate() override;
  
        uint16_t Machine() override { return ehdr.e_machine; }
        uint16_t Type() override { return ehdr.e_type; }
        uint32_t EFlags() override { return ehdr.e_flags; }
        uint32_t ABIVersion() override { return (uint32_t)(ehdr.e_ident[EI_ABIVERSION]); }
  
        GElfStringTable* shstrtab() override;
        GElfStringTable* strtab() override;
        GElfSymbolTable* getSymtab(uint16_t index) override
        {
          return static_cast<GElfSymbolTable*>(section(index));
        }
  
        GElfStringTable* addStringTable(const std::string& name) override;
        GElfStringTable* getStringTable(uint16_t index) override;
  
        GElfSymbolTable* addSymbolTable(const std::string& name, StringTable* stab = 0) override;
        GElfSymbolTable* symtab() override;
  
        GElfSegment* segment(size_t i) override { return segments[i].get(); }
        Segment* segmentByVAddr(uint64_t vaddr) override;
        size_t sectionCount() override { return sections.size(); }
        GElfSection* section(size_t i) override { return sections[i].get(); }
        Section* sectionByVAddr(uint64_t vaddr) override;
        uint16_t machine() const;
        uint16_t etype() const;
        int eclass() const { return elfclass; }
        bool elfError(const char* msg);
  
        GElfNoteSection* note() override;
        GElfNoteSection* addNoteSection(const std::string& name) override;
  
        size_t segmentCount() override { return segments.size(); }
        Segment* initSegment(uint32_t type, uint32_t flags, uint64_t paddr = 0) override;
        bool addSegments() override;
  
        Section* addSection(const std::string &name,
                            uint32_t type,
                            uint64_t flags = 0,
                            uint64_t entsize = 0,
                            Segment* segment = 0) override;
  
        RelocationSection* addRelocationSection(Section* sec, SymbolTable* symtab);
        RelocationSection* relocationSection(Section* sec, SymbolTable* symtab = 0) override;
  
      private:
        bool frozen;
        int elfclass;
        FileImage img;
        const char* buffer;
        size_t bufferSize;
        Elf* e;
        GElf_Ehdr ehdr;
        GElfStringTable* shstrtabSection;
        GElfStringTable* strtabSection;
        GElfSymbolTable* symtabSection;
        GElfNoteSection* noteSection;
        std::vector<std::unique_ptr<GElfSegment>> segments;
        std::vector<std::unique_ptr<GElfSection>> sections;
  
        bool imgError();
        const char *elfError();
        bool elfBegin(Elf_Cmd cmd);
        bool elfEnd();
        bool push0();
        bool pullElf();
  
        friend class GElfSection;
        friend class GElfSymbolTable;
        friend class GElfNoteSection;
        friend class GElfRelocationSection;
        friend class GElfSegment;
        friend class GElfSymbol;
      };
  
      GElfSegment::GElfSegment(GElfImage* elf_, uint16_t index_)
        : elf(elf_),
          index(index_)
      {
        memset(&phdr, 0, sizeof(phdr));
      }
  
      GElfSegment::GElfSegment(GElfImage* elf_, uint16_t index_,
        uint32_t type, uint32_t flags, uint64_t paddr)
        : elf(elf_),
          index(index_)
      {
        memset(&phdr, 0, sizeof(phdr));
        phdr.p_type = type;
        phdr.p_flags = flags;
        phdr.p_paddr = paddr;
      }
  
      const char* GElfSegment::data() const
      {
        return (const char*) elf->data() + phdr.p_offset;
      }
  
      bool GElfImage::Freeze()
      {
        assert(!frozen);
        if (!push()) { return false; }
        frozen = true;
        return true;
      }
  
      bool GElfImage::Validate()
      {
        if (ELFMAG0 != ehdr.e_ident[EI_MAG0] ||
            ELFMAG1 != ehdr.e_ident[EI_MAG1] ||
            ELFMAG2 != ehdr.e_ident[EI_MAG2] ||
            ELFMAG3 != ehdr.e_ident[EI_MAG3]) {
          out << "Invalid ELF magic" << std::endl;
          return false;
        }
        if (EV_CURRENT != ehdr.e_version) {
          out << "Invalid ELF version" << std::endl;
          return false;
        }
        return true;
      }
  
      bool GElfSegment::push(uint64_t vaddr)
      {
        phdr.p_align = 0;
        phdr.p_offset = 0;
        if (!sections.empty()) {
          phdr.p_offset = sections[0]->offset();
        }
        for (Section* section : sections) {
          phdr.p_align = (std::max)(phdr.p_align, section->memAlign());
        }
        phdr.p_vaddr = alignUp(vaddr, (std::max)(phdr.p_align, (uint64_t) 1));
        phdr.p_filesz = 0;
        phdr.p_memsz = 0;
        for (Section* section : sections) {
          phdr.p_memsz = alignUp(phdr.p_memsz, (std::max)(section->memAlign(), (uint64_t) 1));
          phdr.p_filesz = alignUp(phdr.p_filesz, (std::max)(section->memAlign(), (uint64_t) 1));
          if (!section->updateAddr(phdr.p_vaddr + phdr.p_memsz)) { return false; }
          phdr.p_filesz += (section->type() == SHT_NOBITS) ? 0 : section->size();
          phdr.p_memsz += section->memSize();
        }
        if (!gelf_update_phdr(elf->e, index, &phdr)) { return elf->elfError("gelf_update_phdr failed"); }
        return true;
      }
  
      bool GElfSegment::pull()
      {
        if (!gelf_getphdr(elf->e, index, &phdr)) { return elf->elfError("gelf_getphdr failed"); }
        return true;
      }
  
      uint16_t GElfSegment::getSegmentIndex()
      {
        return index;
      }
  
      bool GElfSegment::updateAddSection(Section *section)
      {
        sections.push_back(section);
        return true;
      }
  
      GElfSection::GElfSection(GElfImage* elf_)
        : elf(elf_),
          memsize_(0),
          align_(0),
          reloc_sec(nullptr),
          ndxscn(0)
      {
      }
  
      uint16_t GElfSection::getSectionIndex() const
      {
        return (uint16_t)ndxscn;
      }
  
      std::string GElfSection::Name() const
      {
        return std::string(elf->shstrtab()->getString(hdr.sh_name));
      }
  
      bool GElfSection::updateAddr(uint64_t addr)
      {
        Elf_Scn *scn = elf_getscn(elf->e, ndxscn);
        assert(scn);
        if (!gelf_getshdr(scn, &hdr)) { return elf->elfError("gelf_get_shdr failed"); }
        hdr.sh_addr = addr;
        if (!gelf_update_shdr(scn, &hdr)) { return elf->elfError("gelf_update_shdr failed"); }
        return true;
      }
  
      bool GElfSection::push(const char* name, uint32_t shtype, uint64_t shflags, uint16_t shlink, uint32_t info, uint32_t align, uint64_t entsize)
      {
        Elf_Scn *scn = elf_newscn(elf->e);
        if (!scn) { return false; }
        ndxscn = elf_ndxscn(scn);
        if (!gelf_getshdr(scn, &hdr)) { return elf->elfError("gelf_get_shdr failed"); }
        align = (std::max)(align, (uint32_t) 8);
        hdr.sh_name = elf->shstrtab()->addString1(name);
        hdr.sh_type = shtype;
        hdr.sh_flags = shflags;
        hdr.sh_link = shlink;
        hdr.sh_addr = 0;
        hdr.sh_info = info;
        hdr.sh_addralign = align;
        hdr.sh_entsize = entsize;
        if (!gelf_update_shdr(scn, &hdr)) { return elf->elfError("gelf_update_shdr failed"); }
        return true;
      }
  
      bool GElfSection::pull0()
      {
        Elf_Scn *scn = elf_getscn(elf->e, ndxscn);
        if (!scn) { return false; }
        if (!gelf_getshdr(scn, &hdr)) { return elf->elfError("gelf_get_shdr failed"); }
        return true;
      }
  
      bool GElfSection::pull(uint16_t ndx)
      {
        ndxscn = (size_t) ndx;
        if (!pull0()) { return false; }
        Elf_Scn *scn = elf_getscn(elf->e, ndx);
        if (!scn) { return false; }
        Elf_Data *edata0 = elf_getdata(scn, NULL);
        if (edata0) {
          data0 = Buffer((const Buffer::byte_type*)edata0->d_buf, edata0->d_size, edata0->d_align);
        }
        seg = elf->segmentByVAddr(hdr.sh_addr);
        return true;
      }
  
      bool GElfSection::push()
      {
        Elf_Scn *scn = elf_getscn(elf->e, ndxscn);
        assert(scn);
        Elf_Data *edata = nullptr;
        edata = elf_newdata(scn);
        if (!edata) { return elf->elfError("elf_newdata failed"); }
        if (hdr.sh_type == SHT_NOBITS) {
          edata->d_buf = 0;
          edata->d_size = memsize_;
          if (align_ != 0) {
            edata->d_align = align_;
          }
        } else {
          edata->d_buf = (void*)data.raw();
          edata->d_size = data.size();
          if (data.align() != 0) {
            edata->d_align = data.align();
          }
        }
        edata->d_align = (std::max)(edata->d_align, (uint64_t) 8);
        switch (hdr.sh_type) {
        case SHT_RELA:
          edata->d_type = ELF_T_RELA;
          break;
        case SHT_SYMTAB:
          edata->d_type = ELF_T_SYM;
          break;
        default:
          edata->d_type = ELF_T_BYTE;
          break;
        }
        edata->d_version = EV_CURRENT;
        if (!gelf_getshdr(scn, &hdr)) { return elf->elfError("gelf_get_shdr failed"); }
        hdr.sh_size = edata->d_size;
        hdr.sh_addralign = edata->d_align;
        if (!gelf_update_shdr(scn, &hdr)) { return elf->elfError("gelf_update_shdr failed"); }
        return true;
      }
  
      uint64_t GElfSection::nextDataOffset(uint64_t align) const
      {
        return data.nextOffset(align);
      }
  
      uint64_t GElfSection::addData(const void *src, uint64_t size, uint64_t align)
      {
        return data.add(src, size, align);
      }
  
      bool GElfSection::getData(uint64_t offset, void* dest, uint64_t size)
      {
        Elf_Data* edata = 0;
        uint64_t coffset = 0;
        uint64_t csize = 0;
        Elf_Scn *scn = elf_getscn(elf->e, ndxscn);
        assert(scn);
        if ((edata = elf_getdata(scn, edata)) != 0) {
          if (coffset <= offset && offset <= coffset + edata->d_size) {
            csize = (std::min)(size, edata->d_size - offset);
            memcpy(dest, (const char*) edata->d_buf + offset - coffset, csize);
            coffset += csize;
            dest = (char*) dest + csize;
            size -= csize;
            if (!size) { return true; }
          }
        }
        return false;
      }
  
      RelocationSection* GElfSection::relocationSection(SymbolTable* symtab)
      {
        if (!reloc_sec) {
          reloc_sec = elf->addRelocationSection(this, symtab);
        }
        return reloc_sec;
      }
  
      GElfStringTable::GElfStringTable(GElfImage* elf)
        : GElfSection(elf)
      {
      }
  
      bool GElfStringTable::push(const char* name, uint32_t shtype, uint64_t shflags)
      {
        if (!GElfSection::push(name, shtype, shflags, SHN_UNDEF, 0, 0)) { return false; }
        return true;
      }
  
      bool GElfStringTable::pullData()
      {
        return true;
      }
  
      const char* GElfStringTable::addString(const std::string& s)
      {
        if (data0.size() == 0 && data.size() == 0) {
          data.add('\0');
        }
        return data.get<const char*>(data.addString(s));
      }
  
      size_t GElfStringTable::addString1(const std::string& s)
      {
        if (data0.size() == 0 && data.size() == 0) {
          data.add('\0');
        }
        return data.addString(s);
      }
  
      const char* GElfStringTable::getString(size_t ndx)
      {
        if (data0.has(ndx)) { return data0.get<const char*>(ndx); }
        else if (data.has(ndx)) { return data.get<const char*>(ndx); }
        return nullptr;
      }
  
      size_t GElfStringTable::getStringIndex(const char* s)
      {
        if (data0.has(s)) {
          return data0.getOffset(s);
        } else if (data.has(s)) {
          return data.getOffset(s);
        } else {
          assert(false);
          return 0;
        }
      }
  
      GElfSymbol::GElfSymbol(GElfSymbolTable* symtab_, Buffer &data_, size_t index_)
        : symtab(symtab_),
          edata(data_),
          eindex(index_)
      {
      }
  
      Section* GElfSymbol::section()
      {
        if (Sym()->st_shndx != SHN_UNDEF) {
          return symtab->elf->section(Sym()->st_shndx);
        }
        return 0;
      }
  
      bool GElfSymbol::push(const std::string& name, uint64_t value, uint64_t size, unsigned char type, unsigned char binding, uint16_t shndx, unsigned char other)
      {
        Sym()->st_name = symtab->strtab->addString1(name.c_str());
        Sym()->st_value = value;
        Sym()->st_size = size;
        Sym()->st_info = GELF_ST_INFO(binding, type);
        Sym()->st_shndx = shndx;
        Sym()->st_other = other;
        return true;
      }
  
      std::string GElfSymbol::name()
      {
        return symtab->strtab->getString(Sym()->st_name);
      }
  
      GElfSymbolTable::GElfSymbolTable(GElfImage* elf)
        : GElfSection(elf),
          strtab(0)
      {
      }
  
      bool GElfSymbolTable::push(const char* name, GElfStringTable* strtab)
      {
        if (!strtab) { strtab = elf->strtab(); }
        this->strtab = strtab;
        if (!GElfSection::push(name, SHT_SYMTAB, 0, strtab->getSectionIndex(), 0, 0, sizeof(Elf64_Sym))) { return false;  }
        return true;
      }
  
      bool GElfSymbolTable::pullData()
      {
        strtab = elf->getStringTable(hdr.sh_link);
        for (size_t i = 0; i < data0.size() / sizeof(GElf_Sym); ++i) {
          symbols.push_back(std::unique_ptr<GElfSymbol>(new GElfSymbol(this, data0, i * sizeof(GElf_Sym))));
        }
        return true;
      }
  
      Symbol* GElfSymbolTable::addSymbolInternal(Section* section, const std::string& name, uint64_t value, uint64_t size, unsigned char type, unsigned char binding, unsigned char other)
      {
        GElfSymbol *sym = new (std::nothrow) GElfSymbol(this, data, data.reserve<GElf_Sym>());
        uint16_t shndx = section ? section->getSectionIndex() : (uint16_t) SHN_UNDEF;
        if (!sym->push(name, value, size, type, binding, shndx, other)) {
          delete sym;
          return nullptr;
        }
        symbols.push_back(std::unique_ptr<GElfSymbol>(sym));
        return sym;
      }
  
      Symbol* GElfSymbolTable::addSymbol(Section* section, const std::string& name, uint64_t value, uint64_t size, unsigned char type, unsigned char binding, unsigned char other)
      {
        if (symbols.size() == 0) {
          this->addSymbolInternal(nullptr, "", 0, 0, 0, 0, 0);
        }
        return this->addSymbolInternal(section, name, value, size, type, binding, other);
      }
  
      size_t GElfSymbolTable::symbolCount()
      {
        return symbols.size();
      }
  
      Symbol* GElfSymbolTable::symbol(size_t i)
      {
        return symbols[i].get();
      }
  
      GElfNoteSection::GElfNoteSection(GElfImage* elf)
        : GElfSection(elf)
      {
      }
  
      bool GElfNoteSection::push(const std::string& name)
      {
        return GElfSection::push(name.c_str(), SHT_NOTE, 0, 0, 0, 8);
      }
  
      bool GElfNoteSection::addNote(const std::string& name, uint32_t type, const void* desc, uint32_t desc_size)
      {
        data.addStringLength(name, NOTE_RECORD_ALIGNMENT);
        data.add(desc_size, NOTE_RECORD_ALIGNMENT);
        data.add(type, NOTE_RECORD_ALIGNMENT);
        data.addString(name, NOTE_RECORD_ALIGNMENT);
        data.align(NOTE_RECORD_ALIGNMENT);
        if (desc_size > 0) {
          assert(desc);
          data.add(desc, desc_size, NOTE_RECORD_ALIGNMENT);
          data.align(NOTE_RECORD_ALIGNMENT);
        }
        return true;
      }
  
      bool GElfNoteSection::getNote(const std::string& name, uint32_t type, void** desc, uint32_t* desc_size)
      {
        Elf_Data* data = 0;
        Elf_Scn *scn = elf_getscn(elf->e, ndxscn);
        assert(scn);
        while ((data = elf_getdata(scn, data)) != 0) {
          uint32_t note_offset = 0;
          while (note_offset < data->d_size) {
            char* notec = (char *) data->d_buf + note_offset;
            Elf64_Nhdr* note = (Elf64_Nhdr*) notec;
            if (type == note->n_type) {
              std::string note_name = GetNoteString(note->n_namesz, notec + sizeof(Elf64_Nhdr));
              if (name == note_name) {
                *desc = notec + sizeof(Elf64_Nhdr) + alignUp(note->n_namesz, 4);
                *desc_size = note->n_descsz;
                return true;
              }
            }
            note_offset += sizeof(Elf64_Nhdr) + alignUp(note->n_namesz, 4) + alignUp(note->n_descsz, 4);
          }
        }
        return false;
      }
  
      bool GElfRelocation::push(uint32_t type, Symbol* symbol, uint64_t offset, int64_t addend)
      {
        Rela()->r_info = GELF_R_INFO((uint64_t) symbol->index(), type);
        Rela()->r_offset = offset;
        Rela()->r_addend = addend;
        return true;
      }
  
      RelocationSection* GElfRelocation::section()
      {
        return rsection;
      }
  
      Symbol* GElfRelocation::symbol()
      {
        return rsection->symtab->symbol(symbolIndex());
      }
  
      GElfRelocationSection::GElfRelocationSection(GElfImage* elf, Section* section_, GElfSymbolTable* symtab_)
        : GElfSection(elf),
          section(section_),
          symtab(symtab_)
      {
      }
  
      bool GElfRelocationSection::push(const std::string& name)
      {
        return GElfSection::push(name.c_str(), SHT_RELA, 0, symtab->getSectionIndex(), section->getSectionIndex(), 0, sizeof(Elf64_Rela));
      }
  
      Relocation* GElfRelocationSection::addRelocation(uint32_t type, Symbol* symbol, uint64_t offset, int64_t addend)
      {
        GElfRelocation *rela = new (std::nothrow) GElfRelocation(this, data, data.reserve<GElf_Rela>());
        if (!rela || !rela->push(type, symbol, offset, addend)) {
          delete rela;
          return nullptr;
        }
        relocations.push_back(std::unique_ptr<GElfRelocation>(rela));
        return rela;
      }
  
      bool GElfRelocationSection::pullData()
      {
        section = elf->section(hdr.sh_info);
        symtab = elf->getSymtab(hdr.sh_link);
        Elf_Scn *lScn = elf_getscn(elf->e, ndxscn);
        assert(lScn);
        Elf_Data *lData = elf_getdata(lScn, nullptr);
        assert(lData);
        data0 = Buffer((const Buffer::byte_type*)lData->d_buf, lData->d_size, lData->d_align);
        for (size_t i = 0; i < data0.size() / sizeof(GElf_Rela); ++i) {
          relocations.push_back(std::unique_ptr<GElfRelocation>(new GElfRelocation(this, data0, i * sizeof(GElf_Rela))));
        }
        return true;
      }
  
      GElfImage::GElfImage(int elfclass_)
        : frozen(true),
          elfclass(elfclass_),
          buffer(0), bufferSize(0),
          e(0),
          shstrtabSection(0), strtabSection(0),
          symtabSection(0),
          noteSection(0)
      {
        if (EV_NONE == elf_version(EV_CURRENT)) {
          assert(false);
        }
      }
  
      GElfImage::~GElfImage()
      {
        elf_end(e);
      }
  
      bool GElfImage::imgError()
      {
        out << img.output();
        return false;
      }
  
      const char *GElfImage::elfError()
      {
        return elf_errmsg(-1);
      }
  
      bool GElfImage::elfBegin(Elf_Cmd cmd)
      {
        if ((e = elf_begin(img.fd(), cmd, NULL
  #ifdef AMD_LIBELF
                         , NULL
  #endif
          )) == NULL) {
          out << "elf_begin failed: " << elfError() << std::endl;
          return false;
        }
        return true;
      }
  
      bool GElfImage::initNew(uint16_t machine, uint16_t type, uint8_t os_abi, uint8_t abi_version, uint32_t e_flags)
      {
        if (!img.create()) { return imgError(); }
        if (!elfBegin(ELF_C_WRITE)) { return false; }
        if (!gelf_newehdr(e, elfclass)) { return elfError("gelf_newehdr failed"); }
        if (!gelf_getehdr(e, &ehdr)) { return elfError("gelf_getehdr failed"); }
        ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
        ehdr.e_ident[EI_VERSION] = EV_CURRENT;
        ehdr.e_ident[EI_OSABI] = os_abi;
        ehdr.e_ident[EI_ABIVERSION] = abi_version;
        ehdr.e_machine = machine;
        ehdr.e_type = type;
        ehdr.e_version = EV_CURRENT;
        ehdr.e_flags = e_flags;
        if (!gelf_update_ehdr(e, &ehdr)) { return elfError("gelf_updateehdr failed"); }
        sections.push_back(std::unique_ptr<GElfSection>());
        if (!shstrtab()->push(".shstrtab", SHT_STRTAB, SHF_STRINGS)) { return elfError("Failed to create shstrtab"); }
        ehdr.e_shstrndx = shstrtab()->getSectionIndex();
        if (!gelf_update_ehdr(e, &ehdr)) { return elfError("gelf_updateehdr failed"); }
        if (!strtab()->push(".strtab", SHT_STRTAB, SHF_STRINGS)) { return elfError("Failed to create strtab"); }
        frozen = false;
        return true;
      }
  
      bool GElfImage::loadFromFile(const std::string& filename)
      {
        if (!img.create()) { return imgError(); }
        if (!img.readFrom(filename)) { return imgError(); }
        if (!elfBegin(ELF_C_RDWR)) { return false; }
        return pullElf();
      }
  
      bool GElfImage::saveToFile(const std::string& filename)
      {
        if (buffer) {
          std::ofstream out(filename.c_str(), std::ios::binary);
          if (out.fail()) { return false; }
          out.write(buffer, bufferSize);
          return !out.fail();
        } else {
          if (!push()) { return false; }
          return img.writeTo(filename);
        }
      }
  
      bool GElfImage::initFromBuffer(const void* buffer, size_t size)
      {
        if (size == 0) { size = ElfSize(buffer); }
        if (!img.create()) { return imgError(); }
        if (!img.copyFrom(buffer, size)) { return imgError(); }
        if (!elfBegin(ELF_C_RDWR)) { return false; }
        return pullElf();
      }
  
      bool GElfImage::initAsBuffer(const void* buffer, size_t size)
      {
        if (size == 0) { size = ElfSize(buffer); }
        if ((e = elf_memory(reinterpret_cast<char*>(const_cast<void*>(buffer)), size
  #ifdef AMD_LIBELF
                         , NULL
  #endif
          )) == NULL) {
          out << "elf_begin(buffer) failed: " << elfError() << std::endl;
          return false;
        }
        this->buffer = reinterpret_cast<const char*>(buffer);
        this->bufferSize = size;
        return pullElf();
      }
  
      bool GElfImage::pullElf()
      {
        if (!gelf_getehdr(e, &ehdr)) { return elfError("gelf_getehdr failed"); }
        segments.reserve(ehdr.e_phnum);
        for (size_t i = 0; i < ehdr.e_phnum; ++i) {
          GElfSegment* segment = new GElfSegment(this, i);
          segment->pull();
          segments.push_back(std::unique_ptr<GElfSegment>(segment));
        }
  
        shstrtabSection = new GElfStringTable(this);
        if (!shstrtabSection->pull(ehdr.e_shstrndx)) { return false; }
        Elf_Scn* scn = 0;
        for (unsigned n = 0; n < ehdr.e_shnum; ++n) {
          scn = elf_getscn(e, n);
          if (n == ehdr.e_shstrndx) {
            sections.push_back(std::unique_ptr<GElfSection>(shstrtabSection));
            continue;
          }
          GElf_Shdr shdr;
          if (!gelf_getshdr(scn, &shdr)) { return elfError("Failed to get shdr"); }
          GElfSection* section = 0;
          if (shdr.sh_type == SHT_NOTE) {
            section = new GElfNoteSection(this);
          } else if (shdr.sh_type == SHT_RELA) {
            section = new GElfRelocationSection(this);
          } else if (shdr.sh_type == SHT_STRTAB) {
            section = new GElfStringTable(this);
          } else if (shdr.sh_type == SHT_SYMTAB || shdr.sh_type == SHT_DYNSYM) {
            section = new GElfSymbolTable(this);
          } else if (shdr.sh_type == SHT_NULL) {
            section = 0;
            sections.push_back(std::unique_ptr<GElfSection>());
          } else {
            section = new GElfSection(this);
          }
          if (section) {
            sections.push_back(std::unique_ptr<GElfSection>(section));
            if (!section->pull(n)) { return false; }
          }
        }
  
        for (size_t n = 1; n < sections.size(); ++n) {
          GElfSection* section = sections[n].get();
          if (section->type() == SHT_STRTAB) {
            if (!section->pullData()) { return false; }
          }
        }
  
        for (size_t n = 1; n < sections.size(); ++n) {
          GElfSection* section = sections[n].get();
          if (section->type() == SHT_SYMTAB || section->type() == SHT_DYNSYM) {
            if (!section->pullData()) { return false; }
          }
        }
  
        for (size_t n = 1; n < sections.size(); ++n) {
          GElfSection* section = sections[n].get();
          if (section->type() != SHT_STRTAB && section->type() != SHT_SYMTAB && section->type() != SHT_DYNSYM) {
            if (!section->pullData()) { return false; }
          }
        }
  
        for (size_t i = 1; i < sections.size(); ++i) {
          if (i == ehdr.e_shstrndx || i == ehdr.e_shstrndx) { continue; }
          std::unique_ptr<GElfSection>& section = sections[i];
          if (section->Name() == ".strtab") { strtabSection = static_cast<GElfStringTable*>(section.get()); }
          if (section->Name() == ".symtab") { symtabSection = static_cast<GElfSymbolTable*>(section.get()); }
          if (section->Name() == ".note") { noteSection = static_cast<GElfNoteSection*>(section.get()); }
        }
  
        size_t phnum;
        if (elf_getphdrnum(e, &phnum) < 0) { return elfError("elf_getphdrnum failed"); }
        for (size_t i = 0; i < phnum; ++i) {
          segments.push_back(std::unique_ptr<GElfSegment>(new GElfSegment(this, i)));
          if (!segments[i]->pull()) { return false; }
        }
  
        return true;
      }
  
      bool GElfImage::elfError(const char* msg)
      {
        out << "Error: " << msg << ": " << elfError() << std::endl;
        return false;
      }
  
      uint64_t GElfImage::size()
      {
        if (buffer) {
          return ElfSize(buffer);
        } else {
          return img.getSize();
        }
      }
  
      bool GElfImage::push0()
      {
        assert(e);
        for (std::unique_ptr<GElfSection>& section : sections) {
          if (section && !section->push()) { return false; }
        }
  
        for (std::unique_ptr<GElfSection>& section : sections) {
          if (section && !section->pull0()) { return false; }
        }
  
        if (!segments.empty()) {
          if (!gelf_newphdr(e, segments.size())) { return elfError("gelf_newphdr failed"); }
        }
        if (elf_update(e, ELF_C_NULL) < 0) { return elfError("elf_update (1.1) failed"); }
        if (!segments.empty()) {
          for (std::unique_ptr<GElfSection>& section : sections) {
            // Update section offsets.
            if (section && !section->pull0()) { return false; }
          }
          uint64_t vaddr = 0;
          for (std::unique_ptr<GElfSegment>& segment : segments) {
            if (!segment->push(vaddr)) { return false; }
            vaddr = segment->vaddr() + segment->memSize();
          }
        }
        return true;
      }
  
      bool GElfImage::push()
      {
        if (!push0()) { return false; }
        if (elf_update(e, ELF_C_WRITE) < 0) { return elfError("elf_update (2) failed"); }
        return true;
      }
  
      Segment* GElfImage::segmentByVAddr(uint64_t vaddr)
      {
        for (std::unique_ptr<GElfSegment>& seg : segments) {
          if (seg->vaddr() <= vaddr && vaddr < seg->vaddr() + seg->memSize()) {
            return seg.get();
          }
        }
        return 0;
      }
  
      Section* GElfImage::sectionByVAddr(uint64_t vaddr)
      {
        for (size_t n = 1; n < sections.size(); ++n) {
          if (sections[n]->addr() <= vaddr && vaddr < sections[n]->addr() + sections[n]->size()) {
            return sections[n].get();
          }
        }
        return nullptr;
      }
  
      bool GElfImage::elfEnd()
      {
        return false;
      }
  
      bool GElfImage::writeTo(const std::string& filename)
      {
        if (!img.writeTo(filename)) { return imgError(); }
        return true;
      }
  
      bool GElfImage::copyToBuffer(void** buf, size_t* size)
      {
        if (buffer) {
          *buf = malloc(bufferSize);
          memcpy(*buf, buffer, bufferSize);
          if (size) { *size = bufferSize; }
          return true;
        } else {
          return img.copyTo(buf, size);
        }
      }
  
      bool GElfImage::copyToBuffer(void* buf, size_t size)
      {
        if (buffer) {
          if (size < bufferSize) { return false; }
          memcpy(buf, buffer, bufferSize);
          return true;
        } else {
          return img.copyTo(buf, size);
        }
      }
  
      GElfStringTable* GElfImage::addStringTable(const std::string& name) 
      {
        GElfStringTable* stab = new GElfStringTable(this);
        sections.push_back(std::unique_ptr<GElfStringTable>(stab));
        return stab;
      }
  
      GElfStringTable* GElfImage::getStringTable(uint16_t index)
      {
        return static_cast<GElfStringTable*>(sections[index].get());
      }
  
      GElfSymbolTable* GElfImage::addSymbolTable(const std::string& name, StringTable* stab)
      {
        if (!stab) { stab = strtab(); }
        const char* name0 = shstrtab()->addString(name);
        GElfSymbolTable* symtab = new GElfSymbolTable(this);
        symtab->push(name0, static_cast<GElfStringTable*>(stab));
        sections.push_back(std::unique_ptr<GElfSection>(symtab));
        return symtab;
      }
  
      GElfStringTable* GElfImage::shstrtab() {
        if (!shstrtabSection) {
          shstrtabSection = addStringTable(".shstrtab");
        }
        return shstrtabSection;
      }
  
      GElfStringTable* GElfImage::strtab() {
        if (!strtabSection) {
          strtabSection = addStringTable(".shstrtab");
        }
        return strtabSection;
      }
  
      GElfSymbolTable* GElfImage::symtab()
      {
        if (!symtabSection) {
          symtabSection = addSymbolTable(".symtab", strtab());
        }
        return symtabSection;
      }
  
  
      GElfNoteSection* GElfImage::note()
      {
        if (!noteSection) { noteSection = addNoteSection(".note"); }
        return noteSection;
      }
  
      GElfNoteSection* GElfImage::addNoteSection(const std::string& name)
      {
        GElfNoteSection* note = new GElfNoteSection(this);
        note->push(name);
        sections.push_back(std::unique_ptr<GElfSection>(note));
        return note;
      }
  
      Segment* GElfImage::initSegment(uint32_t type, uint32_t flags, uint64_t paddr)
      {
        GElfSegment *seg = new (std::nothrow) GElfSegment(this, segments.size(), type, flags, paddr);
        segments.push_back(std::unique_ptr<GElfSegment>(seg));
        return seg;
      }
  
      bool GElfImage::addSegments()
      {
        return true;
      }
  
      Section* GElfImage::addSection(const std::string &name,
                                     uint32_t type,
                                     uint64_t flags,
                                     uint64_t entsize, Segment* segment)
      {
        GElfSection *section = new (std::nothrow) GElfSection(this);
        if (!section || !section->push(name.c_str(), type, flags, 0, 0, 0, entsize)) {
          delete section;
          return nullptr;
        }
        if (segment) {
          if (!segment->updateAddSection(section)) {
            delete section;
            return nullptr;
          }
        }
        sections.push_back(std::unique_ptr<GElfSection>(section));
        return section;
      }
  
      RelocationSection* GElfImage::addRelocationSection(Section* sec, SymbolTable* symtab)
      {
        std::string section_name = ".rela" + sec->Name();
        if (!symtab) { symtab = this->symtab(); }
        GElfRelocationSection *rsec = new GElfRelocationSection(this, sec, (GElfSymbolTable*) symtab);
        if (!rsec || !rsec->push(section_name)) {
          delete rsec;
          return nullptr;
        }
        sections.push_back(std::unique_ptr<GElfRelocationSection>(rsec));
        return rsec;
      }
  
      RelocationSection* GElfImage::relocationSection(Section* sec, SymbolTable* symtab)
      {
        return sec->relocationSection(symtab);
      }
  
      uint16_t GElfImage::machine() const
      {
        return ehdr.e_machine;
      }
  
      uint16_t GElfImage::etype() const
      {
        return ehdr.e_type;
      }
  
      Image* NewElf32Image() { return new GElfImage(ELFCLASS32); }
      Image* NewElf64Image() { return new GElfImage(ELFCLASS64); }
  
      uint64_t ElfSize(const void* emi)
      {
        const Elf64_Ehdr *ehdr = (const Elf64_Ehdr*) emi;
        if (NULL == ehdr || EV_CURRENT != ehdr->e_version) {
          return false;
        }
  
        const Elf64_Shdr *shdr = (const Elf64_Shdr*)((char*)emi + ehdr->e_shoff);
        if (NULL == shdr) {
          return false;
        }
  
        uint64_t max_offset = ehdr->e_shoff;
        uint64_t total_size = max_offset + ehdr->e_shentsize * ehdr->e_shnum;
  
        for (uint16_t i = 0; i < ehdr->e_shnum; ++i) {
          uint64_t cur_offset = static_cast<uint64_t>(shdr[i].sh_offset);
          if (max_offset < cur_offset) {
            max_offset = cur_offset;
            total_size = max_offset;
            if (SHT_NOBITS != shdr[i].sh_type) {
              total_size += static_cast<uint64_t>(shdr[i].sh_size);
            }
          }
        }
  
        return total_size;
      }
  
      std::string GetNoteString(uint32_t s_size, const char* s)
      {
        if (!s_size) { return ""; }
        if (s[s_size-1] == '\0') {
          return std::string(s, s_size-1);
        } else {
          return std::string(s, s_size);
        }
      }
  
    }
  }
  