/* linux.h -- common stuff for the Linux stub loaders

   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>
 */


// NOTE:
//   to avoid endless problems with moving libc and kernel headers, this
//   section is now completely freestanding


#if defined(__GNUC__)
#  if defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__)
#    define ACC_CC_GNUC         (__GNUC__ * 0x10000L + __GNUC_MINOR__ * 0x100 + __GNUC_PATCHLEVEL__)
#  elif defined(__GNUC_MINOR__)
#    define ACC_CC_GNUC         (__GNUC__ * 0x10000L + __GNUC_MINOR__ * 0x100)
#  else
#    define ACC_CC_GNUC         (__GNUC__ * 0x10000L)
#  endif
#endif

#define ACC_UNUSED(var)         ((void) var)


/*************************************************************************
//
**************************************************************************/

// <stddef.h>
typedef long ptrdiff_t;
typedef long ssize_t;
typedef unsigned long size_t;
// <stdint.h>
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned uint32_t;
#if (ACC_CC_GNUC >= 0x020800ul)
__extension__ typedef long long int64_t;
__extension__ typedef unsigned long long uint64_t;
#elif defined(_WIN32)
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
typedef long long int64_t;
typedef unsigned long long uint64_t;
#endif
typedef size_t uintptr_t;

// <sys/types.h>
typedef long off_t;
//typedef int64_t off64_t;
//typedef size_t caddr_t;
typedef void* caddr_t;
typedef unsigned pid_t;

struct rusage;
struct timex;
struct timeval {
    unsigned tv_sec;
    unsigned tv_usec;
};
struct timespec {
    unsigned tv_sec;
    long tv_nsec;
};


// misc constants

#if defined(__powerpc64__)  //{
// __powerpc64__ hardware uses 64KiB in 64-bit mode, 4KiB in 32-bit mode.
// EXCEPT qemu-ppc64 uses 4KiB in 64-bit mode, too.
// So PAGE_SIZE is not constant in 64-bit mode of __powerpc64__.
    // empty: not constant, must use run-time value from AT_PAGESZ
#elif defined(__mips__)  //}{
#define PAGE_MASK       (~0ul<<16)   /* discards the offset, keeps the page */
#define PAGE_SIZE       ( 1ul<<16)
#elif defined(__amd64__)  //}{
#define PAGE_MASK       (~0ul<<12)   /* discards the offset, keeps the page */
#define PAGE_SIZE       ( 1ul<<12)
#elif defined(__i386__) || defined(__powerpc__) || defined(__arm__)  //}{
#define PAGE_MASK       (~0ul<<12)   /* discards the offset, keeps the page */
#define PAGE_SIZE       ( 1ul<<12)
#endif  //}

#define SEEK_SET        0
#define SEEK_CUR        1
#define SEEK_END        2

#define O_RDONLY        00
#define O_WRONLY        01
#define O_RDWR          02
#define O_CREAT         0100
#define O_EXCL          0200

#define R_OK            4
#define W_OK            2
#define X_OK            1
#define F_OK            0

#define F_GETFD         1
#define F_SETFD         2
#define FD_CLOEXEC      1

// <errno.h>
#define ENOENT          2
#define EINTR           4

// <sys/mman.h>
#define PROT_READ       0x1
#define PROT_WRITE      0x2
#define PROT_EXEC       0x4
#define PROT_NONE       0x0

#define MAP_SHARED      0x01
#define MAP_PRIVATE     0x02
#define MAP_FIXED       0x10
#define MAP_ANONYMOUS   0x20
#define MAP_DENYWRITE 0x0800  /* ETXTBSY */

// <linux/prctl.h>
// These should enable removal of PT_LOAD[1] for setting brk(0).
// "git blame linux/kernel/sys.c" says:
// 028ee4be34a09 (Cyrill Gorcunov        2012-01-12 17:20:55 -0800 2157)   case PR_SET_MM_START_BRK:
#define PR_SET_MM               35
# define PR_SET_MM_START_BRK            6
# define PR_SET_MM_BRK                  7

/*************************************************************************
// i386 syscalls
//
// Because of different <asm/unistd.h> versions and subtle bugs
// in both gcc and egcs we define all syscalls manually.
//
// Also, errno conversion is not necessary in our case, and we
// use optimized assembly statements to further decrease the size.
**************************************************************************/

#if defined(__i386__)  /*{*/

// <asm/unistd.h>
#define __NR_exit                 1
#define __NR_fork                 2
#define __NR_read                 3
#define __NR_write                4
#define __NR_open                 5
#define __NR_close                6
#define __NR_waitpid              7
#define __NR_link                 9
#define __NR_unlink              10
#define __NR_execve              11
#define __NR_lseek               19
#define __NR_getpid              20
#define __NR_access              33
#define __NR_mkdir               39
#define __NR_brk                 45
#define __NR_fcntl               55
#define __NR_gettimeofday        78
#define __NR_mmap                90
#define __NR_munmap              91
#define __NR_ftruncate           93
#define __NR_stat               106
#define __NR_uname              122
#define __NR_adjtimex           124
#define __NR_mprotect           125
#define __NR_msync              144
#define __NR_nanosleep          162
#define __NR_memfd_create       356  /*0x164*/

#undef _syscall0
#undef _syscall1
#undef _syscall2
#undef _syscall3
#ifndef __NR__exit
#  define __NR__exit __NR_exit
#endif

#define Z0(x)    (__builtin_constant_p(x) && (long)(x) == 0)
#define Z1(x)    (__builtin_constant_p(x) && (long)(x) >= -128 && (long)(x) <= 127)

#define _syscall0(type,name) \
type name(void) \
{ \
    long __res; \
    if (Z1(__NR_##name)) { \
        __asm__ __volatile__ ("push %[sysN]; popl %0; int $0x80" \
            : "=a" (__res) \
            : [sysN] "g" (__NR_##name)); \
    } else { \
        __asm__ __volatile__ ("int $0x80" \
            : "=a" (__res) \
            : [sysN] "a" (__NR_##name)); \
    } \
    return (type) __res; \
}

#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) \
{ \
    long __res, junk; \
    if (Z1(__NR_##name)) { \
        if (Z0(arg1)) { \
            __asm__ __volatile__ ("xorl %%ebx,%%ebx; push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res) \
                : [sysN] "g" (__NR_##name) \
                : "ebx"); \
        } else if (Z1(arg1)) { \
            __asm__ __volatile__ ("push %[a1]; popl %%ebx; push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res) \
                : [sysN] "g" (__NR_##name), [a1] "g" ((long)(arg1)) \
                : "ebx"); \
        } else { \
            __asm__ __volatile__ ("push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res), "=b" (junk) \
                : [sysN] "g" (__NR_##name),"b" ((long)(arg1))); \
        } \
    } else { \
        __asm__ __volatile__ ("int $0x80" \
            : "=a" (__res), "=b" (junk) \
            : "a" (__NR_##name),"b" ((long)(arg1))); \
    } \
    return (type) __res; \
}

#define _syscall1noreturn(name,type1,arg1) \
void __attribute__((__noreturn__,__nothrow__)) name(type1 arg1) \
{ \
    if (Z1(__NR_##name)) { \
        if (Z0(arg1)) { \
            __asm__ __volatile__ ("xorl %%ebx,%%ebx; push %0; popl %%eax; int $0x80" \
              : : "g" (__NR_##name) ); \
        } else if (Z1(arg1)) { \
            __asm__ __volatile__ ("push %[a1]; popl %%ebx; push %[sysN]; popl %%eax; int $0x80" \
              : : [sysN] "g" (__NR_##name), [a1] "g" ((long)(arg1)) ); \
        } else { \
            __asm__ __volatile__ ("push %0; popl %%eax; int $0x80" \
              : : "g" (__NR_##name),"b" ((long)(arg1))); \
        } \
    } else { \
        __asm__ __volatile__ ("int $0x80" \
            : : "a" (__NR_##name),"b" ((long)(arg1))); \
    } \
    for(;;) ; \
}

#define _syscall2(type,name,type1,arg1,type2,arg2) \
type name(type1 arg1,type2 arg2) \
{ \
    long __res, junkb, junkc; \
    if (Z1(__NR_##name)) { \
        if (Z0(arg1) && Z0(arg2)) { \
            __asm__ __volatile__ ("xorl %%ecx,%%ecx; xorl %%ebx,%%ebx; push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res) \
                : [sysN] "g" (__NR_##name) \
                : "ebx", "ecx"); \
        } else if (Z0(arg1) && Z1(arg2)) { \
            __asm__ __volatile__ ("push %[a2]; popl %%ecx; xorl %%ebx,%%ebx; push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res) \
                : [sysN] "g" (__NR_##name), [a2] "g" ((long)(arg2)) \
                : "ebx", "ecx"); \
        } else if (Z1(arg1) && Z0(arg2)) { \
            __asm__ __volatile__ ("xorl %%ecx,%%ecx; push %[a1]; popl %%ebx; push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res) \
                : [sysN] "g" (__NR_##name), [a1] "g" ((long)(arg1)) \
                : "ebx", "ecx"); \
        } else if (Z1(arg1) && Z1(arg2)) { \
            __asm__ __volatile__ ("push %[a2]; popl %%ecx; push %[a1]; popl %%ebx; push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res) \
                : [sysN] "g" (__NR_##name), [a1] "g" ((long)(arg1)), [a2] "g" ((long)(arg2)) \
                : "ebx", "ecx"); \
        } else if (Z0(arg1)) { \
            __asm__ __volatile__ ("xorl %%ebx,%%ebx; push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res), "=c" (junkc) \
                : [sysN] "g" (__NR_##name),"c" ((long)(arg2)) \
                : "ebx"); \
        } else if (Z0(arg2)) { \
            __asm__ __volatile__ ("push %[sysN]; popl %0; xorl %%ecx,%%ecx; int $0x80" \
                : "=a" (__res), "=b" (junkb) \
                : [sysN] "g" (__NR_##name),"b" ((long)(arg1)) \
                : "ecx"); \
        } else if (Z1(arg1)) { \
            __asm__ __volatile__ ("push %[sysN]; popl %0; push %[a1]; popl %%ebx; int $0x80" \
                : "=a" (__res), "=c" (junkc) \
                : [sysN] "g" (__NR_##name), [a1] "g" ((long)(arg1)),"c" ((long)(arg2)) \
                : "ebx"); \
        } else if (Z1(arg2)) { \
            __asm__ __volatile__ ("push %[sysN]; popl %0; push %[a2]; popl %%ecx; int $0x80" \
                : "=a" (__res), "=b" (junkb) \
                : [sysN] "g" (__NR_##name),"b" ((long)(arg1)), [a2] "g" ((long)(arg2)) \
                : "ecx"); \
        } else { \
            __asm__ __volatile__ ("push %[sysN]; popl %0; int $0x80" \
                : "=a" (__res), "=b" (junkb), "=c" (junkc) \
                : [sysN] "g" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
        } \
    } else { \
        __asm__ __volatile__ ("int $0x80" \
            : "=a" (__res), "=b" (junkb), "=c" (junkc) \
            : [sysN] "a" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
    } \
    return (type) __res; \
}

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name(type1 arg1,type2 arg2,type3 arg3) \
{ \
    long __res, junkb, junkc, junkd; \
    if (Z1(__NR_##name)) { \
        __asm__ __volatile__ ("push %[sysN]; popl %0; int $0x80" \
            : "=a" (__res), "=b" (junkb), "=c" (junkc), "=d" (junkd) \
            : [sysN] "g" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
                                "d" ((long)(arg3))); \
    } else { \
        __asm__ __volatile__ ("int $0x80" \
            : "=a" (__res), "=b" (junkb), "=c" (junkc), "=d" (junkd) \
            : [sysN] "a" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
                                "d" ((long)(arg3))); \
    } \
    return (type) __res; \
}


#define _exit           syscall_exit
#define exit            syscall_exit

struct stat;
struct utsname;

static inline _syscall2(int,access,const char *,file,int,mode)
static inline _syscall1(int,adjtimex,struct timex *,ntx)
static inline _syscall1(void *,brk,void *,high)
static inline _syscall1(int,close,int,fd)
static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
static inline _syscall1noreturn(_exit,int,exitcode)
static inline _syscall3(int,fcntl,int,fd,int,cmd,long,arg)
static inline _syscall0(pid_t,fork)
static inline _syscall2(int,ftruncate,int,fd,size_t,len)
static inline _syscall0(pid_t,getpid)
static inline _syscall2(int,gettimeofday,struct timeval *,tv,void *,tz)
static inline _syscall3(off_t,lseek,int,fd,off_t,offset,int,whence)
static inline _syscall2(int,memfd_create,char const *,name,unsigned,flags);
static inline _syscall2(int,mkdir,char const *,name,unsigned,mode);
static inline _syscall3(int,mprotect,void *,addr,size_t,len,int,prot)
static inline _syscall3(int,msync,void const *,addr,size_t,len,unsigned,flags)
static inline _syscall2(int,munmap,void *,start,size_t,length)
static inline _syscall2(int,nanosleep,const struct timespec *,rqtp,struct timespec *,rmtp)
static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
static inline _syscall3(ssize_t,read,int,fd,void *,buf,size_t,count)
static inline _syscall2(int,stat,char const *,path,struct stat *,buf)
static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
static inline _syscall3(ssize_t,write,int,fd,const void *,buf,size_t,count)
static inline _syscall1(int,uname,struct utsname *,buf)
static inline _syscall1(int,unlink,const char *,file)
static inline _syscall2(int,link,const char *,src, const char *,dst)

#undef Z0
#undef Z1

#else  /*}{ generic */

void *brk(void *);
int close(int);
void exit(int) __attribute__((__noreturn__,__nothrow__));
int ftruncate(int fd, size_t len);
off_t lseek(int fd, off_t offset, int whence);
int memfd_create(char const *, unsigned);
int munmap(void *, size_t);
int mprotect(void const *, size_t, int);
int msync(void const *, size_t, unsigned);
int open(char const *, int, int);
int openat(int fd, char const *, unsigned, unsigned);
ssize_t read(int, void *, size_t);
int unlink(char const *);
ssize_t write(int, void const *, size_t);

#endif  /*}*/
void __clear_cache(void *, void *);
void *mmap(void *, size_t, int, int, int, off_t);

#if defined(__mips__)  /*}{*/
  #undef  MAP_ANONYMOUS
  #define MAP_ANONYMOUS 0x800
#endif  //}

/*************************************************************************
// <elf.h>
**************************************************************************/

typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;
typedef uint32_t Elf32_Word;
typedef int32_t  Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef int32_t  Elf64_Sword;
typedef uint64_t Elf32_Xword;
typedef int64_t  Elf32_Sxword;
typedef uint64_t Elf64_Xword;
typedef int64_t  Elf64_Sxword;
typedef uint32_t Elf32_Addr;
typedef uint64_t Elf64_Addr;
typedef uint32_t Elf32_Off;
typedef uint64_t Elf64_Off;
typedef uint16_t Elf32_Section;
typedef uint16_t Elf64_Section;
typedef Elf32_Half Elf32_Versym;
typedef Elf64_Half Elf64_Versym;


#define EI_NIDENT 16

typedef struct
{
  unsigned char e_ident[EI_NIDENT];
  Elf32_Half    e_type;
  Elf32_Half    e_machine;
  Elf32_Word    e_version;
  Elf32_Addr    e_entry;
  Elf32_Off     e_phoff;
  Elf32_Off     e_shoff;
  Elf32_Word    e_flags;
  Elf32_Half    e_ehsize;
  Elf32_Half    e_phentsize;
  Elf32_Half    e_phnum;
  Elf32_Half    e_shentsize;
  Elf32_Half    e_shnum;
  Elf32_Half    e_shstrndx;
} Elf32_Ehdr;

typedef struct
{
  unsigned char e_ident[EI_NIDENT];
  Elf64_Half    e_type;
  Elf64_Half    e_machine;
  Elf64_Word    e_version;
  Elf64_Addr    e_entry;
  Elf64_Off     e_phoff;
  Elf64_Off     e_shoff;
  Elf64_Word    e_flags;
  Elf64_Half    e_ehsize;
  Elf64_Half    e_phentsize;
  Elf64_Half    e_phnum;
  Elf64_Half    e_shentsize;
  Elf64_Half    e_shnum;
  Elf64_Half    e_shstrndx;
} Elf64_Ehdr;

typedef struct
{
  Elf32_Word    p_type;
  Elf32_Off     p_offset;
  Elf32_Addr    p_vaddr;
  Elf32_Addr    p_paddr;
  Elf32_Word    p_filesz;
  Elf32_Word    p_memsz;
  Elf32_Word    p_flags;
  Elf32_Word    p_align;
} Elf32_Phdr;

typedef struct
{
  Elf64_Word    p_type;
  Elf64_Word    p_flags;
  Elf64_Off     p_offset;
  Elf64_Addr    p_vaddr;
  Elf64_Addr    p_paddr;
  Elf64_Xword   p_filesz;
  Elf64_Xword   p_memsz;
  Elf64_Xword   p_align;
} Elf64_Phdr;

typedef struct
{
  uint32_t a_type;
  union {
      uint32_t a_val;
  } a_un;
} Elf32_auxv_t;

typedef struct
{
  uint64_t a_type;
  union
    {
      uint64_t a_val;
    } a_un;
} Elf64_auxv_t;

#define AT_NULL         0
#define AT_IGNORE       1
#define AT_PHDR         3
#define AT_PHENT        4
#define AT_PHNUM        5
#define AT_PAGESZ       6
#define AT_BASE         7
#define AT_ENTRY        9
#define AT_RANDOM       25 /* Address of 16 random bytes.  */

#define ET_EXEC         2
#define ET_DYN          3

#define PF_X            1
#define PF_W            2
#define PF_R            4

#define PT_LOAD         1
#define PT_INTERP       3
#define PT_PHDR         6


/*************************************************************************
// UPX stuff
**************************************************************************/

// !!! must be the same as in p_unix.h !!!
#define OVERHEAD        2048

#define UPX_MAGIC_LE32  0x21585055          /* "UPX!" */

#if 1
// patch constants for our loader (le32 format)
//#define UPX1            0x31585055          /* "UPX1" */
#define UPX2            0x32585055          /* "UPX2" */
#define UPX3            0x33585055          /* "UPX4" */
#define UPX4            0x34585055          /* "UPX4" */
//#define UPX5            0x35585055          /* "UPX5" */
#else
// transform into relocations when using ElfLinker
extern const unsigned UPX2;
extern const unsigned UPX3;
extern const unsigned UPX4;
#define UPX2    ((unsigned) (const void *) &UPX2)
#define UPX3    ((unsigned) (const void *) &UPX3)
#define UPX4    ((unsigned) (const void *) &UPX4)
#endif


typedef int nrv_int;
typedef int nrv_int32;
typedef unsigned int nrv_uint;
typedef unsigned int nrv_uint32;
#define nrv_byte unsigned char
#define nrv_bytep unsigned char *
#define nrv_voidp void *


// From ../p_unix.h
struct b_info {     // 12-byte header before each compressed block
    uint32_t sz_unc;            // uncompressed_size
    uint32_t sz_cpr;            // compressed_size
    unsigned char b_method;     // compression algorithm
    unsigned char b_ftid;       // filter id
    unsigned char b_cto8;       // filter parameter
    unsigned char b_unused;
};

struct l_info       // 12-byte trailer in header for loader (offset 116)
{
    uint32_t l_checksum;
    uint32_t l_magic;
    uint16_t l_lsize;
    uint8_t  l_version;
    uint8_t  l_format;
};

struct p_info       // 12-byte packed program header follows stub loader
{
    uint32_t p_progid;
    uint32_t p_filesize;
    uint32_t p_blocksize;
};


#define CONST_CAST(type, var) \
    ((type) ((uintptr_t) (var)))


#if !defined(__attribute_cdecl)
#if defined(__i386__)
#  if (ACC_CC_GNUC >= 0x030300)
#    define __attribute_cdecl   __attribute__((__cdecl__, __used__))
#  elif (ACC_CC_GNUC >= 0x020700)
#    define __attribute_cdecl   __attribute__((__cdecl__))
#  endif
#endif
#endif
#if !defined(__attribute_cdecl)
#  define __attribute_cdecl /*empty*/
#endif

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