/* Copyright (c) 2005-2008, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 * copyright notice, this list of conditions and the following disclaimer
 * in the documentation and/or other materials provided with the
 * distribution.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ---
 * Author: Markus Gutschke, Carl Crous
 */

#ifndef _ELFCORE_H
#define _ELFCORE_H
#ifdef __cplusplus
extern "C" {
#endif

/* We currently only support x86-32, x86-64, ARM, and MIPS on Linux.
 * Porting to other related platforms should not be difficult.
 */
#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \
     defined(__mips__)) &&                                                  \
    defined(__linux)

#include <stdarg.h>
#include <stdint.h>
#include <sys/types.h>

/* Define the DUMPER symbol to make sure that there is exactly one
 * core dumper built into the library.
 */
#define DUMPER "ELF"

/* By the time that we get a chance to read CPU registers in the
 * calling thread, they are already in a not particularly useful
 * state. Besides, there will be multiple frames on the stack that are
 * just making the core file confusing. To fix this problem, we take a
 * snapshot of the frame pointer, stack pointer, and instruction
 * pointer at an earlier time, and then insert these values into the
 * core file.
 */

#if defined(__i386__) || defined(__x86_64__)
typedef struct i386_regs { /* Normal (non-FPU) CPU registers            */
#ifdef __x86_64__
#define BP rbp
#define SP rsp
#define IP rip
  uint64_t r15, r14, r13, r12, rbp, rbx, r11, r10;
  uint64_t r9, r8, rax, rcx, rdx, rsi, rdi, orig_rax;
  uint64_t rip, cs, eflags;
  uint64_t rsp, ss;
  uint64_t fs_base, gs_base;
  uint64_t ds, es, fs, gs;
#else
#define BP ebp
#define SP esp
#define IP eip
  uint32_t ebx, ecx, edx, esi, edi, ebp, eax;
  uint16_t ds, __ds, es, __es;
  uint16_t fs, __fs, gs, __gs;
  uint32_t orig_eax, eip;
  uint16_t cs, __cs;
  uint32_t eflags, esp;
  uint16_t ss, __ss;
#endif
} i386_regs;
#elif defined(__ARM_ARCH_3__)
typedef struct arm_regs { /* General purpose registers                 */
#define BP uregs[11] /* Frame pointer                             */
#define SP uregs[13] /* Stack pointer                             */
#define IP uregs[15] /* Program counter                           */
#define LR uregs[14] /* Link register                             */
  long uregs[18];
} arm_regs;
#elif defined(__mips__)
typedef struct mips_regs {
  unsigned long pad[6];    /* Unused padding to match kernel structures */
  unsigned long uregs[32]; /* General purpose registers.                */
  unsigned long hi;        /* Used for multiplication and division.     */
  unsigned long lo;
  unsigned long cp0_epc; /* Program counter.                          */
  unsigned long cp0_badvaddr;
  unsigned long cp0_status;
  unsigned long cp0_cause;
  unsigned long unused;
} mips_regs;
#endif

#if defined(__i386__) && defined(__GNUC__)
/* On x86 we provide an optimized version of the FRAME() macro, if the
 * compiler supports a GCC-style asm() directive. This results in somewhat
 * more accurate values for CPU registers.
 */
typedef struct Frame {
  struct i386_regs uregs;
  int errno_;
  pid_t tid;
} Frame;
#define FRAME(f)                  \
  Frame f;                        \
  do {                            \
    f.errno_ = errno;             \
    f.tid = sys_gettid();         \
    __asm__ volatile(             \
        "push %%ebp\n"            \
        "push %%ebx\n"            \
        "mov  %%ebx,0(%%eax)\n"   \
        "mov  %%ecx,4(%%eax)\n"   \
        "mov  %%edx,8(%%eax)\n"   \
        "mov  %%esi,12(%%eax)\n"  \
        "mov  %%edi,16(%%eax)\n"  \
        "mov  %%ebp,20(%%eax)\n"  \
        "mov  %%eax,24(%%eax)\n"  \
        "mov  %%ds,%%ebx\n"       \
        "mov  %%ebx,28(%%eax)\n"  \
        "mov  %%es,%%ebx\n"       \
        "mov  %%ebx,32(%%eax)\n"  \
        "mov  %%fs,%%ebx\n"       \
        "mov  %%ebx,36(%%eax)\n"  \
        "mov  %%gs,%%ebx\n"       \
        "mov  %%ebx, 40(%%eax)\n" \
        "call 0f\n"               \
        "0:pop %%ebx\n"           \
        "add  $1f-0b,%%ebx\n"     \
        "mov  %%ebx,48(%%eax)\n"  \
        "mov  %%cs,%%ebx\n"       \
        "mov  %%ebx,52(%%eax)\n"  \
        "pushf\n"                 \
        "pop  %%ebx\n"            \
        "mov  %%ebx,56(%%eax)\n"  \
        "mov  %%esp,%%ebx\n"      \
        "add  $8,%%ebx\n"         \
        "mov  %%ebx,60(%%eax)\n"  \
        "mov  %%ss,%%ebx\n"       \
        "mov  %%ebx,64(%%eax)\n"  \
        "pop  %%ebx\n"            \
        "pop  %%ebp\n"            \
        "1:"                      \
        :                         \
        : "a"(&f)                 \
        : "memory");              \
  } while (0)
#define SET_FRAME(f, r) \
  do {                  \
    errno = (f).errno_; \
    (r) = (f).uregs;    \
  } while (0)
#elif defined(__x86_64__) && defined(__GNUC__)
/* The FRAME and SET_FRAME macros for x86_64.  */
typedef struct Frame {
  struct i386_regs uregs;
  int errno_;
  pid_t tid;
} Frame;
#define FRAME(f)                  \
  Frame f;                        \
  do {                            \
    f.errno_ = errno;             \
    f.tid = sys_gettid();         \
    __asm__ volatile(             \
        "push %%rbp\n"            \
        "push %%rbx\n"            \
        "mov  %%r15,0(%%rax)\n"   \
        "mov  %%r14,8(%%rax)\n"   \
        "mov  %%r13,16(%%rax)\n"  \
        "mov  %%r12,24(%%rax)\n"  \
        "mov  %%rbp,32(%%rax)\n"  \
        "mov  %%rbx,40(%%rax)\n"  \
        "mov  %%r11,48(%%rax)\n"  \
        "mov  %%r10,56(%%rax)\n"  \
        "mov  %%r9,64(%%rax)\n"   \
        "mov  %%r8,72(%%rax)\n"   \
        "mov  %%rax,80(%%rax)\n"  \
        "mov  %%rcx,88(%%rax)\n"  \
        "mov  %%rdx,96(%%rax)\n"  \
        "mov  %%rsi,104(%%rax)\n" \
        "mov  %%rdi,112(%%rax)\n" \
        "mov  %%ds,%%rbx\n"       \
        "mov  %%rbx,184(%%rax)\n" \
        "mov  %%es,%%rbx\n"       \
        "mov  %%rbx,192(%%rax)\n" \
        "mov  %%fs,%%rbx\n"       \
        "mov  %%rbx,200(%%rax)\n" \
        "mov  %%gs,%%rbx\n"       \
        "mov  %%rbx,208(%%rax)\n" \
        "call 0f\n"               \
        "0:pop %%rbx\n"           \
        "add  $1f-0b,%%rbx\n"     \
        "mov  %%rbx,128(%%rax)\n" \
        "mov  %%cs,%%rbx\n"       \
        "mov  %%rbx,136(%%rax)\n" \
        "pushf\n"                 \
        "pop  %%rbx\n"            \
        "mov  %%rbx,144(%%rax)\n" \
        "mov  %%rsp,%%rbx\n"      \
        "add  $16,%%ebx\n"        \
        "mov  %%rbx,152(%%rax)\n" \
        "mov  %%ss,%%rbx\n"       \
        "mov  %%rbx,160(%%rax)\n" \
        "pop  %%rbx\n"            \
        "pop  %%rbp\n"            \
        "1:"                      \
        :                         \
        : "a"(&f)                 \
        : "memory");              \
  } while (0)
#define SET_FRAME(f, r)              \
  do {                               \
    errno = (f).errno_;              \
    (f).uregs.fs_base = (r).fs_base; \
    (f).uregs.gs_base = (r).gs_base; \
    (r) = (f).uregs;                 \
  } while (0)
#elif defined(__ARM_ARCH_3__) && defined(__GNUC__)
/* ARM calling conventions are a little more tricky. A little assembly
 * helps in obtaining an accurate snapshot of all registers.
 */
typedef struct Frame {
  struct arm_regs arm;
  int errno_;
  pid_t tid;
} Frame;
#define FRAME(f)                                                     \
  Frame f;                                                           \
  do {                                                               \
    long cpsr;                                                       \
    f.errno_ = errno;                                                \
    f.tid = sys_gettid();                                            \
    __asm__ volatile("stmia %0, {r0-r15}\n" /* All integer regs   */ \
                     :                                               \
                     : "r"(&f.arm)                                   \
                     : "memory");                                    \
    f.arm.uregs[16] = 0;                                             \
    __asm__ volatile("mrs %0, cpsr\n" /* Condition code reg */       \
                     : "=r"(cpsr));                                  \
    f.arm.uregs[17] = cpsr;                                          \
  } while (0)
#define SET_FRAME(f, r)                             \
  do {                                              \
    /* Don't override the FPU status register.   */ \
    /* Use the value obtained from ptrace(). This*/ \
    /* works, because our code does not perform  */ \
    /* any FPU operations, itself.               */ \
    long fps = (f).arm.uregs[16];                   \
    errno = (f).errno_;                             \
    (r) = (f).arm;                                  \
    (r).uregs[16] = fps;                            \
  } while (0)
#elif defined(__mips__) && defined(__GNUC__)
typedef struct Frame {
  struct mips_regs mips_regs;
  int errno_;
  pid_t tid;
} Frame;
#define MIPSREG(n)                            \
  ({                                          \
    register unsigned long r __asm__("$" #n); \
    r;                                        \
  })
#define FRAME(f)                                  \
  Frame f = {0};                                  \
  do {                                            \
    unsigned long hi, lo;                         \
    register unsigned long pc __asm__("$31");     \
    f.mips_regs.uregs[0] = MIPSREG(0);            \
    f.mips_regs.uregs[1] = MIPSREG(1);            \
    f.mips_regs.uregs[2] = MIPSREG(2);            \
    f.mips_regs.uregs[3] = MIPSREG(3);            \
    f.mips_regs.uregs[4] = MIPSREG(4);            \
    f.mips_regs.uregs[5] = MIPSREG(5);            \
    f.mips_regs.uregs[6] = MIPSREG(6);            \
    f.mips_regs.uregs[7] = MIPSREG(7);            \
    f.mips_regs.uregs[8] = MIPSREG(8);            \
    f.mips_regs.uregs[9] = MIPSREG(9);            \
    f.mips_regs.uregs[10] = MIPSREG(10);          \
    f.mips_regs.uregs[11] = MIPSREG(11);          \
    f.mips_regs.uregs[12] = MIPSREG(12);          \
    f.mips_regs.uregs[13] = MIPSREG(13);          \
    f.mips_regs.uregs[14] = MIPSREG(14);          \
    f.mips_regs.uregs[15] = MIPSREG(15);          \
    f.mips_regs.uregs[16] = MIPSREG(16);          \
    f.mips_regs.uregs[17] = MIPSREG(17);          \
    f.mips_regs.uregs[18] = MIPSREG(18);          \
    f.mips_regs.uregs[19] = MIPSREG(19);          \
    f.mips_regs.uregs[20] = MIPSREG(20);          \
    f.mips_regs.uregs[21] = MIPSREG(21);          \
    f.mips_regs.uregs[22] = MIPSREG(22);          \
    f.mips_regs.uregs[23] = MIPSREG(23);          \
    f.mips_regs.uregs[24] = MIPSREG(24);          \
    f.mips_regs.uregs[25] = MIPSREG(25);          \
    f.mips_regs.uregs[26] = MIPSREG(26);          \
    f.mips_regs.uregs[27] = MIPSREG(27);          \
    f.mips_regs.uregs[28] = MIPSREG(28);          \
    f.mips_regs.uregs[29] = MIPSREG(29);          \
    f.mips_regs.uregs[30] = MIPSREG(30);          \
    f.mips_regs.uregs[31] = MIPSREG(31);          \
    __asm__ volatile("mfhi %0" : "=r"(hi));       \
    __asm__ volatile("mflo %0" : "=r"(lo));       \
    __asm__ volatile("jal 1f; 1:nop" : "=r"(pc)); \
    f.mips_regs.hi = hi;                          \
    f.mips_regs.lo = lo;                          \
    f.mips_regs.cp0_epc = pc;                     \
    f.errno_ = errno;                             \
    f.tid = sys_gettid();                         \
  } while (0)
#define SET_FRAME(f, r)                                                 \
  do {                                                                  \
    errno = (f).errno_;                                                 \
    memcpy((r).uregs, (f).mips_regs.uregs, 32 * sizeof(unsigned long)); \
    (r).hi = (f).mips_regs.hi;                                          \
    (r).lo = (f).mips_regs.lo;                                          \
    (r).cp0_epc = (f).mips_regs.cp0_epc;                                \
  } while (0)
#else
/* If we do not have a hand-optimized assembly version of the FRAME()
 * macro, we cannot reliably unroll the stack. So, we show a few additional
 * stack frames for the coredumper.
 */
typedef struct Frame {
  pid_t tid;
} Frame;
#define FRAME(f)          \
  Frame f;                \
  do {                    \
    f.tid = sys_gettid(); \
  } while (0)
#define SET_FRAME(f, r) \
  do {                  \
  } while (0)
#endif

/* Internal function for generating a core file. This API can change without
 * notice and is only supposed to be used internally by the core dumper.
 *
 * This function works for both single- and multi-threaded core
 * dumps. If called as
 *
 *   FRAME(frame);
 *   InternalGetCoreDump(&frame, 0, NULL, ap);
 *
 * it creates a core file that only contains information about the
 * calling thread.
 *
 * Optionally, the caller can provide information about other threads
 * by passing their process ids in "thread_pids". The process id of
 * the caller should not be included in this array. All of the threads
 * must have been attached to with ptrace(), prior to calling this
 * function. They will be detached when "InternalGetCoreDump()" returns.
 *
 * This function either returns a file handle that can be read for obtaining
 * a core dump, or "-1" in case of an error. In the latter case, "errno"
 * will be set appropriately.
 *
 * While "InternalGetCoreDump()" is not technically async signal safe, you
 * might be tempted to invoke it from a signal handler. The code goes to
 * great lengths to make a best effort that this will actually work. But in
 * any case, you must make sure that you preserve the value of "errno"
 * yourself. It is guaranteed to be clobbered otherwise.
 *
 * Also, "InternalGetCoreDump" is not strictly speaking re-entrant. Again,
 * it makes a best effort to behave reasonably when called in a multi-
 * threaded environment, but it is ultimately the caller's responsibility
 * to provide locking.
 */
int InternalGetCoreDump(void *frame, int num_threads, pid_t *thread_pids,
                        va_list ap
                     /* const struct CoreDumpParameters *params,
                        const char *file_name,
                        const char *PATH
                      */);

#endif

#ifdef __cplusplus
}
#endif
#endif /* _ELFCORE_H */
