/*
 * mem_rw.c - 内存读写公共模块
 *
 * 对目标进程的进程空间内存进行读取/写入（经 PTRACE_PEEKDATA/POKEDATA，
 * 按机器字对齐逐字操作）；read_any_mem 额外支持 debugger 侧 out-param
 * 堆副本（local_sym_table）透明直读。
 */

#include "lnxall_trace.h"

/* ========================================================================
 * Memory Read / Write via PTRACE
 * ======================================================================== */

int mem_read(pid_t pid, unsigned long addr, void *buf, size_t len)
{
    DBG("mem_read: pid=%d addr=0x%lx len=%zu", pid, addr, len);
    size_t done = 0;
    while (done < len) {
        unsigned long word_addr = (addr + done) & ~(WORD_SIZE - 1);
        unsigned long offset    = (addr + done) - word_addr;

        errno = 0;
        long word = ptrace(PTRACE_PEEKDATA, pid, word_addr, NULL);
        if (errno != 0) {
            fprintf(stderr, "[mem_read] PTRACE_PEEKDATA addr=0x%lx (word-align 0x%lx): %s\n",
                    addr + done, word_addr, strerror(errno));
            if (done == 0) { perror("PTRACE_PEEKDATA"); return -1; }
            break;
        }
        size_t n = WORD_SIZE - offset;
        if (n > len - done) n = len - done;
        memcpy((char *)buf + done, ((char *)&word) + offset, n);
        done += n;
    }
    return 0;
}

int mem_write(pid_t pid, unsigned long addr, const void *buf, size_t len)
{
    size_t done = 0;
    while (done < len) {
        unsigned long word_addr = (addr + done) & ~(WORD_SIZE - 1);
        unsigned long offset    = (addr + done) - word_addr;
        size_t n = WORD_SIZE - offset;
        if (n > len - done) n = len - done;

        if (n != WORD_SIZE) {
            errno = 0;
            long word = ptrace(PTRACE_PEEKDATA, pid, word_addr, NULL);
            if (errno != 0) { perror("PTRACE_PEEKDATA"); return -1; }
            memcpy(((char *)&word) + offset, (char *)buf + done, n);
            if (ptrace(PTRACE_POKEDATA, pid, word_addr, (void *)word) == -1)
                { perror("PTRACE_POKEDATA"); return -1; }
        } else {
            long word;
            memcpy(&word, (char *)buf + done, sizeof(long));
            if (ptrace(PTRACE_POKEDATA, pid, word_addr, (void *)word) == -1)
                { perror("PTRACE_POKEDATA"); return -1; }
        }
        done += n;
    }
    return 0;
}

int mem_read_word(pid_t pid, unsigned long addr, unsigned long *val)
{
    DBG("mem_read_word: pid=%d addr=0x%lx", pid, addr);
    errno = 0;
    long word = ptrace(PTRACE_PEEKDATA, pid, addr, NULL);
    if (errno != 0) {
        fprintf(stderr, "[mem_read_word] PTRACE_PEEKDATA addr=0x%lx: %s\n",
                addr, strerror(errno));
        perror("PTRACE_PEEKDATA"); return -1;
    }
    *val = (unsigned long)word;
    return 0;
}

/* read_any_mem: 优先尝试把读请求当作 debugger 侧 out-param 堆副本处理
 * (out-param 副本是 gdb 模型的 '<type> <name> = <val>' 创建的，addr 落在
 * 其中一个副本区间内则直接 memcpy，无需与目标进程交互)；否则走 mem_read。
 * 返回约定与 mem_read 一致：0 = 成功。 */
int read_any_mem(unsigned long addr, void *buf, size_t size)
{
    if (size > 0) {
        for (int i = 0; i < g_dbg.local_sym_count; i++) {
            unsigned long a  = g_dbg.local_sym_table[i].addr;
            unsigned long sz = g_dbg.local_sym_table[i].size;
            if (addr >= a &&
                (unsigned long long)addr + size <= (unsigned long long)a + sz) {
                memcpy(buf, (const void *)addr, size);
                return 0;
            }
        }
    }
    return mem_read(g_dbg.target_pid, addr, buf, size);
}
