Fichiers
Mario Limonciello bc5d48e76c Run pre-commit's whitespace related hooks on projects/rocr-runtime (#2130)
* Run pre-commit's whitespace related hooks on projects/rocr-runtime

In order for pre-commit to be useful, everything needs to meet a common
baseline.

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>

* Add missing semicolon which would block compilation on big endian CPUs

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>

---------

Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
2025-12-08 07:56:50 -06:00

1782 lignes
60 KiB
C++

////////////////////////////////////////////////////////////////////////////////
//
// The University of Illinois/NCSA
// Open Source License (NCSA)
//
// Copyright (c) 2014-2020, 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 "core/inc/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 rocr::amd::hsa::alignUp;
namespace rocr {
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) {
_close(in);
return perror("lseek failed");
}
off_t size;
if ((size = _lseek(in, 0L, SEEK_CUR)) < 0) {
_close(in);
return perror("lseek(2) failed");
}
if (_lseek(in, 0L, SEEK_SET) < 0) {
_close(in);
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);
ssize_t bytes_read = _read(d, buffer1, size1);
if (bytes_read < 0) {
free(buffer1);
return perror("read failed");
}
if (static_cast<size_t>(bytes_read) != size1) {
free(buffer1);
return perror("Incomplete read");
}
*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"); }
ssize_t bytes_read = _read(d, buffer, size1);
if (bytes_read < 0) { return perror("read failed"); }
if (static_cast<size_t>(bytes_read) != size1) { return perror("Incomplete read"); }
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]); }
uint32_t EClass() override { return (uint32_t)(ehdr.e_ident[EI_CLASS]); }
uint32_t OsAbi() override { return (uint32_t)(ehdr.e_ident[EI_OSABI]); }
GElfStringTable* shstrtab() override;
GElfStringTable* strtab() override;
GElfSymbolTable* getReferencedSymbolTable(uint16_t index)
{
return static_cast<GElfSymbolTable*>(section(index));
}
GElfSymbolTable* getSymtab(uint16_t index) override
{
if (section(index)->type() == SHT_SYMTAB)
return static_cast<GElfSymbolTable*>(section(index));
return nullptr;
}
GElfSymbolTable* getDynsym(uint16_t index) override
{
if (section(index)->type() == SHT_DYNSYM)
return static_cast<GElfSymbolTable*>(section(index));
return nullptr;
}
GElfSymbolTable* getSymbolTable() override;
GElfSymbolTable* getSymbolTable(uint16_t index) override
{
const char *UseDynsym = getenv("LOADER_USE_DYNSYM");
if (UseDynsym && std::strncmp(UseDynsym, "0", 1) != 0)
return getDynsym(index);
return getSymtab(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;
GElfSymbolTable* dynsym() 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;
GElfSymbolTable* dynsymSection;
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_),
seg(nullptr),
hdr{},
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);
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->getReferencedSymbolTable(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),
dynsymSection(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) { continue; }
std::unique_ptr<GElfSection>& section = sections[i];
if (section->type() == SHT_STRTAB) { strtabSection = static_cast<GElfStringTable*>(section.get()); }
if (section->type() == SHT_SYMTAB) { symtabSection = static_cast<GElfSymbolTable*>(section.get()); }
if (section->type() == SHT_NOTE) { noteSection = static_cast<GElfNoteSection*>(section.get()); }
if (section->type() == SHT_DYNSYM) { dynsymSection = static_cast<GElfSymbolTable*>(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;
}
GElfSymbolTable* GElfImage::dynsym()
{
if (!dynsymSection) {
dynsymSection = addSymbolTable(".dynsym", strtab());
}
return dynsymSection;
}
GElfSymbolTable* GElfImage::getSymbolTable()
{
const char *UseDynsym = getenv("LOADER_USE_DYNSYM");
if (UseDynsym && std::strncmp(UseDynsym, "0", 1) != 0)
return dynsym();
return symtab();
}
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 + static_cast<uint64_t>(ehdr->e_shentsize) * static_cast<uint64_t>(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);
}
}
} // namespace elf
} // namespace amd
} // namespace rocr