/* linker.h --

   This file is part of the UPX executable compressor.

   Copyright (C) Markus Franz Xaver Johannes Oberhumer
   Copyright (C) Laszlo Molnar
   All Rights Reserved.

   UPX and the UCL library are free software; you can redistribute them
   and/or modify them under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer              Laszlo Molnar
   <markus@oberhumer.com>               <ezerotven+github@gmail.com>
 */

#pragma once

/*************************************************************************
// ElfLinker
**************************************************************************/

class ElfLinker /*not_final*/ : private upx::noncopyable {
    friend class Packer;

public:
    const N_BELE_RTP::AbstractPolicy *const bele; // TE - Target Endianness
protected:
    struct Section;
    struct Symbol;
    struct Relocation;

    unsigned e_machine = 0; // EM_NONE
    byte *input = nullptr;
    int inputlen = 0;
    byte *output = nullptr;
    int outputlen = 0;
    unsigned output_capacity = 0;

    Section *head = nullptr;
    Section *tail = nullptr;

    Section **sections = nullptr;
    Symbol **symbols = nullptr;
    Relocation **relocations = nullptr;

    unsigned nsections = 0;
    unsigned nsections_capacity = 0;
    unsigned nsymbols = 0;
    unsigned nsymbols_capacity = 0;
    unsigned nrelocations = 0;
    unsigned nrelocations_capacity = 0;

    bool reloc_done = false;

protected:
    void preprocessSections(char *start, const char *end);
    void preprocessSymbols(char *start, const char *end);
    void preprocessRelocations(char *start, const char *end);
    Section *findSection(const char *name, bool fatal = true) const;

    Symbol *addSymbol(const char *name, const char *section, upx_uint64_t offset);
    Relocation *addRelocation(const char *section, unsigned off, const char *type,
                              const char *symbol, upx_uint64_t add);

public:
    explicit ElfLinker(const N_BELE_RTP::AbstractPolicy *b = &N_BELE_RTP::le_policy) noexcept;
    virtual ~ElfLinker() noexcept;

    void init(unsigned arch, const void *pdata, int plen, unsigned pxtra = 0);
    // virtual void setLoaderAlignOffset(int phase);
    int addLoader(const char *sname);
    void addLoader(const char *s, va_list ap);
#if (ACC_CC_CLANG || ACC_CC_GNUC)
    void addLoaderVA(const char *s, ...) __attribute__((__sentinel__));
#else
    void addLoaderVA(const char *s, ...);
#endif
    Section *addSection(const char *sname, const void *sdata, int slen, unsigned p2align);
    int getSection(const char *sname, int *slen = nullptr) const;
    int getSectionSize(const char *sname) const;
    byte *getLoader(int *llen = nullptr) const;
    void defineSymbol(const char *name, upx_uint64_t value);
    Symbol *findSymbol(const char *name, bool fatal = true) const;
    upx_uint64_t getSymbolOffset(const char *) const;

    void dumpSymbol(const Symbol *, unsigned flags, FILE *fp) const;
    void dumpSymbols(unsigned flags = 0, FILE *fp = nullptr) const;

    void alignWithByte(unsigned len, byte b);
    virtual void alignCode(unsigned len) { alignWithByte(len, 0); }
    virtual void alignData(unsigned len) { alignWithByte(len, 0); }

    // provide overloads to pacify GitHub CodeQL
    void defineSymbol(const char *name, int value) { defineSymbol(name, upx_uint64_t(value)); }
    void defineSymbol(const char *name, unsigned value) { defineSymbol(name, upx_uint64_t(value)); }
    void defineSymbol(const char *name, unsigned long value) {
        defineSymbol(name, upx_uint64_t(value));
    }

protected:
    void relocate();
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type);
};

struct ElfLinker::Section final : private upx::noncopyable {
    char *name = nullptr;
    void *input = nullptr;
    byte *output = nullptr;
    unsigned size = 0;
    unsigned sort_id = 0; // for qsort()
    upx_uint64_t offset = 0;
    unsigned p2align = 0; // log2
    Section *next = nullptr;

    explicit Section(const char *n, const void *i, unsigned s, unsigned a = 0);
    ~Section() noexcept;
};

struct ElfLinker::Symbol final : private upx::noncopyable {
    char *name = nullptr;
    Section *section = nullptr;
    upx_uint64_t offset = 0;

    explicit Symbol(const char *n, Section *s, upx_uint64_t o);
    ~Symbol() noexcept;
};

struct ElfLinker::Relocation final : private upx::noncopyable {
    const Section *section = nullptr;
    unsigned offset = 0;
    const char *type = nullptr;
    const Symbol *value = nullptr;
    upx_uint64_t add = 0; // used in .rela relocations

    explicit Relocation(const Section *s, unsigned o, const char *t, const Symbol *v,
                        upx_uint64_t a);
    ~Relocation() noexcept {}
};

/*************************************************************************
// ElfLinker arch subclasses
**************************************************************************/

class ElfLinkerAMD64 /*not_final*/ : public ElfLinker {
    typedef ElfLinker super;
protected:
    virtual void alignCode(unsigned len) override { alignWithByte(len, 0x90); }
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerArm64LE final : public ElfLinker {
    typedef ElfLinker super;
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerRiscv64LE final : public ElfLinker {
    typedef ElfLinker super;
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerArmBE final : public ElfLinker {
    typedef ElfLinker super;
public:
    explicit ElfLinkerArmBE() noexcept : super(&N_BELE_RTP::be_policy) {}
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerArmLE final : public ElfLinker {
    typedef ElfLinker super;
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerM68k final : public ElfLinker {
    typedef ElfLinker super;
public:
    explicit ElfLinkerM68k() noexcept : super(&N_BELE_RTP::be_policy) {}
protected:
    virtual void alignCode(unsigned len) override;
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerMipsBE final : public ElfLinker {
    typedef ElfLinker super;
public:
    explicit ElfLinkerMipsBE() noexcept : super(&N_BELE_RTP::be_policy) {}
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerMipsLE final : public ElfLinker {
    typedef ElfLinker super;
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerPpc32 final : public ElfLinker {
    typedef ElfLinker super;
public:
    explicit ElfLinkerPpc32() noexcept : super(&N_BELE_RTP::be_policy) {}
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerPpc64 final : public ElfLinker {
    typedef ElfLinker super;
public:
    explicit ElfLinkerPpc64() noexcept : super(&N_BELE_RTP::be_policy) {}
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerPpc64le final : public ElfLinker {
    typedef ElfLinker super;
protected:
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

class ElfLinkerX86 final : public ElfLinker {
    typedef ElfLinker super;
protected:
    virtual void alignCode(unsigned len) override { alignWithByte(len, 0x90); }
    virtual void relocate1(const Relocation *, byte *location, upx_uint64_t value,
                           const char *type) override;
};

/* vim:set ts=4 sw=4 et: */
