| 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. | 
|---|
| 2 | // | 
|---|
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later | 
|---|
| 4 |  | 
|---|
| 5 | /* | 
|---|
| 6 | build: | 
|---|
| 7 | gcc -g -shared -fPIC -O0 -o libinprocdump.so inprocdump.c -ldl | 
|---|
| 8 | */ | 
|---|
| 9 | #include <assert.h> | 
|---|
| 10 | #include <stdbool.h> | 
|---|
| 11 | #include <elf.h> | 
|---|
| 12 | #include <dlfcn.h> | 
|---|
| 13 | #include <fcntl.h> | 
|---|
| 14 | #include <inttypes.h> | 
|---|
| 15 | #include <unistd.h> | 
|---|
| 16 | #include <stdio.h> | 
|---|
| 17 | #include <stdlib.h> | 
|---|
| 18 | #include <string.h> | 
|---|
| 19 | #if defined(__aarch64__) | 
|---|
| 20 | #include "../aarch64/syscall.h" | 
|---|
| 21 | #else | 
|---|
| 22 | #include <syscall.h> | 
|---|
| 23 | #endif | 
|---|
| 24 | #include <sys/mman.h> | 
|---|
| 25 | #include <stdarg.h> | 
|---|
| 26 | #include <sys/auxv.h> | 
|---|
| 27 | #include <linux/futex.h> | 
|---|
| 28 | #include <sys/time.h> | 
|---|
| 29 | #include <errno.h> | 
|---|
| 30 | #include <sys/param.h> | 
|---|
| 31 | #include <sys/socket.h> | 
|---|
| 32 | #include <sys/prctl.h> | 
|---|
| 33 | #include <sys/user.h> | 
|---|
| 34 | #include <linux/audit.h> | 
|---|
| 35 | #include <linux/filter.h> | 
|---|
| 36 | #include <linux/unistd.h> | 
|---|
| 37 | #include <linux/seccomp.h> | 
|---|
| 38 | #include <time.h> | 
|---|
| 39 | #include <sys/un.h> | 
|---|
| 40 |  | 
|---|
| 41 | #include "../include/shared_mem_dump.h" | 
|---|
| 42 | #include "../include/event_man.h" | 
|---|
| 43 | #include "../cpu.h" | 
|---|
| 44 |  | 
|---|
| 45 | #define SAFE_GUARD (1024) | 
|---|
| 46 |  | 
|---|
| 47 | static int g_pid = 0; // store current pid to avoid fork affect | 
|---|
| 48 | static MemoryDumper *g_dumper = NULL; | 
|---|
| 49 | static unsigned char *parameter_flags = NULL; | 
|---|
| 50 | struct number_set trace_set[1]; | 
|---|
| 51 | static int get_stack_range(MemoryDumper *dumper, uintptr_t sp); | 
|---|
| 52 |  | 
|---|
| 53 | long syscall_no_hook(long syscall_number, ...); | 
|---|
| 54 |  | 
|---|
| 55 | static char debug_dumps_on = 0; | 
|---|
| 56 |  | 
|---|
| 57 | void debug_dump(const char *fmt, ...) | 
|---|
| 58 | { | 
|---|
| 59 | int len; | 
|---|
| 60 | va_list ap; | 
|---|
| 61 |  | 
|---|
| 62 | if (!debug_dumps_on) | 
|---|
| 63 | return; | 
|---|
| 64 |  | 
|---|
| 65 | va_start(ap, fmt); | 
|---|
| 66 | len = vsnprintf(NULL, 0, fmt, ap); | 
|---|
| 67 | va_end(ap); | 
|---|
| 68 |  | 
|---|
| 69 | if (len <= 0) | 
|---|
| 70 | return; | 
|---|
| 71 |  | 
|---|
| 72 | char buf[len + 1]; | 
|---|
| 73 |  | 
|---|
| 74 | va_start(ap, fmt); | 
|---|
| 75 | len = vsprintf(buf, fmt, ap); | 
|---|
| 76 | va_end(ap); | 
|---|
| 77 |  | 
|---|
| 78 | syscall_no_hook(SYS_write, 2, buf, len); | 
|---|
| 79 | } | 
|---|
| 80 |  | 
|---|
| 81 | #if defined(__x86_64__) | 
|---|
| 82 | #ifndef SYS_pkey_free | 
|---|
| 83 | #define SYS_pkey_free (331) | 
|---|
| 84 | #endif | 
|---|
| 85 |  | 
|---|
| 86 | static unsigned char syscall_buf[ | 
|---|
| 87 | (SYS_pkey_free - __NR_Linux + 1) * 2 + | 
|---|
| 88 | (SYS_pkey_free - __NR_Linux + 1 + 7) / 8 + 256]; | 
|---|
| 89 |  | 
|---|
| 90 | __asm ( | 
|---|
| 91 | ".global syscall_no_hook\n\t" | 
|---|
| 92 | ".type   syscall_no_hook, @function\n\t" | 
|---|
| 93 | ".text\n\t" | 
|---|
| 94 | "syscall_no_hook:\n\t" | 
|---|
| 95 | "movq %rdi, %rax\n\t"/* convert from linux ABI calling */ | 
|---|
| 96 | "movq %rsi, %rdi\n\t"/* convention to syscall calling convention */ | 
|---|
| 97 | "movq %rdx, %rsi\n\t" | 
|---|
| 98 | "movq %rcx, %rdx\n\t" | 
|---|
| 99 | "movq %r8, %r10 \n\t" | 
|---|
| 100 | "movq %r9, %r8\n\t" | 
|---|
| 101 | "movq 8(%rsp), %r9\n\t" | 
|---|
| 102 | "syscall\n\t" | 
|---|
| 103 | "ret\n\t" | 
|---|
| 104 | ".size syscall_no_hook, .-syscall_no_hook\n\t" | 
|---|
| 105 | ); | 
|---|
| 106 |  | 
|---|
| 107 | #elif defined(__sw_64) | 
|---|
| 108 | #ifndef SYS_execveat | 
|---|
| 109 | #define SYS_execveat (513) | 
|---|
| 110 | #endif | 
|---|
| 111 | static unsigned char syscall_buf[ | 
|---|
| 112 | (SYS_execveat - __NR_Linux + 1) * 2 + | 
|---|
| 113 | (SYS_execveat - __NR_Linux + 1 + 7) / 8 + 256]; | 
|---|
| 114 |  | 
|---|
| 115 | __asm( | 
|---|
| 116 | ".global syscall_no_hook\n\t" | 
|---|
| 117 | ".type   syscall_no_hook, @function\n\t" | 
|---|
| 118 | ".text\n\t" | 
|---|
| 119 | "syscall_no_hook:\n\t" | 
|---|
| 120 | "bis $31, $16, $0\n\t" | 
|---|
| 121 | "bis $31, $17, $16\n\t" | 
|---|
| 122 | "bis $31, $18, $17\n\t" | 
|---|
| 123 | "bis $31, $19, $18\n\t" | 
|---|
| 124 | "bis $31, $20, $19\n\t" | 
|---|
| 125 | "bis $31, $21, $20\n\t" | 
|---|
| 126 | "ldl $21, 0($30)\n\t"// 6th argument of syscall | 
|---|
| 127 | "sys_call 0x83\n\t"// sys_call never change ra register | 
|---|
| 128 | "ret $31, ($26), 0x1\n\t" | 
|---|
| 129 | ".size syscall_no_hook, .-syscall_no_hook\n\t" | 
|---|
| 130 | ); | 
|---|
| 131 |  | 
|---|
| 132 | #elif defined(__mips64) | 
|---|
| 133 | #ifndef SYS_pkey_free | 
|---|
| 134 | #define SYS_pkey_free (325 + __NR_Linux) | 
|---|
| 135 | #endif | 
|---|
| 136 |  | 
|---|
| 137 | static unsigned char syscall_buf[ | 
|---|
| 138 | (SYS_pkey_free - __NR_Linux + 1) * 2 + | 
|---|
| 139 | (SYS_pkey_free - __NR_Linux + 1 + 7) / 8 + 256]; | 
|---|
| 140 |  | 
|---|
| 141 | __asm( | 
|---|
| 142 | ".globl syscall_no_hook\n\t" | 
|---|
| 143 | "syscall_no_hook:\n\t" | 
|---|
| 144 | "move $v0, $a0\n\t" | 
|---|
| 145 | "move $a0, $a1\n\t" | 
|---|
| 146 | "move $a1, $a2\n\t" | 
|---|
| 147 | "move $a2, $a3\n\t" | 
|---|
| 148 | "move $a3, $a4\n\t" | 
|---|
| 149 | "move $a4, $a5\n\t" | 
|---|
| 150 | "move $a5, $a6\n\t" | 
|---|
| 151 | "syscall\n\t" | 
|---|
| 152 | "jr $ra\n\t" | 
|---|
| 153 | "nop\n\t" | 
|---|
| 154 | ); | 
|---|
| 155 | #elif defined(__aarch64__) | 
|---|
| 156 | #ifndef SYS_fork | 
|---|
| 157 | #define SYS_fork (1079 + __NR_Linux) | 
|---|
| 158 | #endif | 
|---|
| 159 |  | 
|---|
| 160 | static unsigned char syscall_buf[ | 
|---|
| 161 | (SYS_fork - __NR_Linux + 1) * 2 + | 
|---|
| 162 | (SYS_fork - __NR_Linux + 1 + 7) / 8 + 256]; | 
|---|
| 163 |  | 
|---|
| 164 | __asm( | 
|---|
| 165 | ".globl syscall_no_hook\n\t" | 
|---|
| 166 | "syscall_no_hook:\n\t" | 
|---|
| 167 | "mov x8, x0\n\t" | 
|---|
| 168 | "mov x0, x1\n\t" | 
|---|
| 169 | "mov x1, x2\n\t" | 
|---|
| 170 | "mov x2, x3\n\t" | 
|---|
| 171 | "mov x3, x4\n\t" | 
|---|
| 172 | "mov x4, x5\n\t" | 
|---|
| 173 | "mov x5, x6\n\t" | 
|---|
| 174 | "svc #0x0\n\t" | 
|---|
| 175 | "ret\n\t" | 
|---|
| 176 | "nop \n\t" | 
|---|
| 177 | "nop\n\t" | 
|---|
| 178 | "nop\n\t" | 
|---|
| 179 | "nop\n\t" | 
|---|
| 180 | "nop\n\t" | 
|---|
| 181 | "nop\n\t" | 
|---|
| 182 | "nop\n\t" | 
|---|
| 183 | ); | 
|---|
| 184 | #else | 
|---|
| 185 | #error need define new arch | 
|---|
| 186 | #endif | 
|---|
| 187 |  | 
|---|
| 188 | static unsigned char number_isset(const unsigned int i, const number_slot_t *const vec) | 
|---|
| 189 | { | 
|---|
| 190 | return (vec[i / BITS_PER_SLOT] & | 
|---|
| 191 | ((number_slot_t)1 << (i % BITS_PER_SLOT))) > 0; | 
|---|
| 192 | } | 
|---|
| 193 |  | 
|---|
| 194 | unsigned char is_number_in_set(const unsigned number) | 
|---|
| 195 | { | 
|---|
| 196 | if (0 == trace_set[0].nslots) { | 
|---|
| 197 | return trace_set[0].not_flag; | 
|---|
| 198 | } | 
|---|
| 199 | return (((number / BITS_PER_SLOT < trace_set[0].nslots) && | 
|---|
| 200 | number_isset(number, trace_set[0].vec)) ^ trace_set[0].not_flag); | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | static int open_socket(int *sock_fd) | 
|---|
| 204 | { | 
|---|
| 205 | struct sockaddr_un addr; | 
|---|
| 206 | int sc, rc = -1; | 
|---|
| 207 |  | 
|---|
| 208 | *sock_fd = syscall_no_hook(SYS_socket, AF_UNIX, SOCK_STREAM, 0); | 
|---|
| 209 | if (*sock_fd == -1) { | 
|---|
| 210 | debug_dump( "failed to create socket: %s\n", strerror(errno)); | 
|---|
| 211 | goto done; | 
|---|
| 212 | } | 
|---|
| 213 |  | 
|---|
| 214 | memset(&addr, 0, sizeof(addr)); | 
|---|
| 215 | addr.sun_family = AF_UNIX; | 
|---|
| 216 | snprintf(addr.sun_path, sizeof(addr.sun_path) - 1, | 
|---|
| 217 | SHARED_SOCKET_NAME, g_pid); | 
|---|
| 218 | debug_dump( "socket: %s -> %d\n", addr.sun_path, *sock_fd); | 
|---|
| 219 |  | 
|---|
| 220 | sc = syscall_no_hook(SYS_connect, | 
|---|
| 221 | *sock_fd, (struct sockaddr *)&addr, sizeof(addr)); | 
|---|
| 222 | if (sc == -1) { | 
|---|
| 223 | syscall_no_hook(SYS_close, *sock_fd); | 
|---|
| 224 | debug_dump( "failed to connect: %s\n", strerror(errno)); | 
|---|
| 225 | goto done; | 
|---|
| 226 | } | 
|---|
| 227 |  | 
|---|
| 228 | rc = 0; | 
|---|
| 229 |  | 
|---|
| 230 | done: | 
|---|
| 231 | return rc; | 
|---|
| 232 | } | 
|---|
| 233 |  | 
|---|
| 234 | int send_cmd(int cmd, uintptr_t arg) | 
|---|
| 235 | { | 
|---|
| 236 | char buf[20]; | 
|---|
| 237 | int sock_fd = 0; | 
|---|
| 238 | int nbytes = 0; | 
|---|
| 239 |  | 
|---|
| 240 | *(int *)&buf[0] = cmd; | 
|---|
| 241 | *(uintptr_t *)&buf[4] = arg; | 
|---|
| 242 |  | 
|---|
| 243 | if (open_socket(&sock_fd) < 0) { | 
|---|
| 244 | return -1; | 
|---|
| 245 | } | 
|---|
| 246 |  | 
|---|
| 247 | nbytes = syscall_no_hook(SYS_write, | 
|---|
| 248 | sock_fd, buf, sizeof(int) + sizeof(uintptr_t)); | 
|---|
| 249 | debug_dump( "send_cmd:%d, %lx->%d\n", cmd, arg, nbytes); | 
|---|
| 250 |  | 
|---|
| 251 | // get result | 
|---|
| 252 | *(int *)&buf[0] = -1; | 
|---|
| 253 | nbytes = syscall_no_hook(SYS_read, sock_fd, buf, sizeof(int)); | 
|---|
| 254 | syscall_no_hook(SYS_close, sock_fd); | 
|---|
| 255 |  | 
|---|
| 256 | return *(int *)&buf[0]; | 
|---|
| 257 | } | 
|---|
| 258 |  | 
|---|
| 259 | MemoryDumper *get_memory_dumper(void) | 
|---|
| 260 | { | 
|---|
| 261 | return g_dumper; | 
|---|
| 262 | } | 
|---|
| 263 |  | 
|---|
| 264 | MemoryDumper *create_memory_dumper(void) | 
|---|
| 265 | { | 
|---|
| 266 | char path[256]; | 
|---|
| 267 | int fd = 0; | 
|---|
| 268 | void *map_addr = NULL; | 
|---|
| 269 | int flags = MAP_SHARED; | 
|---|
| 270 | MemoryDumper *dumper = NULL; | 
|---|
| 271 | int size = 0; | 
|---|
| 272 | char *data = NULL; | 
|---|
| 273 | char *shared_buffer_size = NULL; | 
|---|
| 274 | int nonce = send_cmd(SYS_share_name, 0); | 
|---|
| 275 | if (nonce < 0) { | 
|---|
| 276 | return NULL; | 
|---|
| 277 | } | 
|---|
| 278 |  | 
|---|
| 279 | snprintf(path, sizeof(path) - 1, | 
|---|
| 280 | SHARED_FILE_NAME, g_pid, nonce); | 
|---|
| 281 | shared_buffer_size = getenv( "ST2_SYSCALL_BUFFER_SIZE"); | 
|---|
| 282 | if (shared_buffer_size) { | 
|---|
| 283 | size = atoi(shared_buffer_size); | 
|---|
| 284 | if (size < SHARED_BUFFER_SIZE) size = SHARED_BUFFER_SIZE; | 
|---|
| 285 | } else { | 
|---|
| 286 | size = SHARED_BUFFER_SIZE; | 
|---|
| 287 | } | 
|---|
| 288 |  | 
|---|
| 289 | #if defined (__aarch64__) | 
|---|
| 290 | fd = syscall_no_hook(SYS_openat, AT_FDCWD, path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); | 
|---|
| 291 | #else | 
|---|
| 292 | fd = syscall_no_hook(SYS_open, path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0600); | 
|---|
| 293 | #endif | 
|---|
| 294 | if (fd < 0) { | 
|---|
| 295 | return NULL; // xabort_errno(errno, "Failed to open shmem file!\n"); | 
|---|
| 296 | } | 
|---|
| 297 | debug_dump( "shmem file:%s, size=%d\n", path, size); | 
|---|
| 298 |  | 
|---|
| 299 | send_cmd(SYS_init_buffers, size); | 
|---|
| 300 |  | 
|---|
| 301 | map_addr = (void *)syscall_no_hook(SYS_mmap, | 
|---|
| 302 | NULL, size, PROT_READ | PROT_WRITE, flags, fd, 0); | 
|---|
| 303 | syscall_no_hook(SYS_close, fd); | 
|---|
| 304 | // Remove the fs name so that we don't have to worry about | 
|---|
| 305 | // cleaning up this segment in error conditions. | 
|---|
| 306 | syscall_no_hook(SYS_unlink, path); | 
|---|
| 307 |  | 
|---|
| 308 | if (MAP_FAILED == map_addr) { | 
|---|
| 309 | return NULL; // xabort_errno(errno, "Failed to mmap shmem region\n!"); | 
|---|
| 310 | } | 
|---|
| 311 |  | 
|---|
| 312 | dumper = (MemoryDumper *)map_addr; | 
|---|
| 313 |  | 
|---|
| 314 | dumper->pid = g_pid; | 
|---|
| 315 | dumper->mutex = 1; | 
|---|
| 316 | assert(dumper->size + sizeof(*dumper) == (unsigned)size); | 
|---|
| 317 |  | 
|---|
| 318 | // load syscall filter set | 
|---|
| 319 | trace_set[0].nslots = dumper->syscall.nslots; | 
|---|
| 320 | trace_set[0].not_flag = dumper->syscall.not_flag; | 
|---|
| 321 | data = BUFFER_HEAD(dumper); | 
|---|
| 322 | size = sizeof(number_slot_t) * trace_set[0].nslots; | 
|---|
| 323 | if (trace_set[0].nslots > 0) { | 
|---|
| 324 | trace_set[0].vec = (number_slot_t *)&syscall_buf[0]; | 
|---|
| 325 | memcpy(trace_set[0].vec, data, size); | 
|---|
| 326 | data += size; | 
|---|
| 327 | } else { | 
|---|
| 328 | trace_set[0].vec = NULL; | 
|---|
| 329 | } | 
|---|
| 330 | parameter_flags = &syscall_buf[0] + size; | 
|---|
| 331 | memcpy(parameter_flags, data + sizeof(int), *(int *)data); | 
|---|
| 332 |  | 
|---|
| 333 | send_cmd(SYS_enable_dump, 0); | 
|---|
| 334 |  | 
|---|
| 335 | g_dumper = dumper; | 
|---|
| 336 |  | 
|---|
| 337 | return dumper; | 
|---|
| 338 | } | 
|---|
| 339 |  | 
|---|
| 340 | void lock_buffers(MemoryDumper *dumper) | 
|---|
| 341 | { | 
|---|
| 342 | int s; | 
|---|
| 343 | // int tid = syscall_no_hook(SYS_gettid); | 
|---|
| 344 | // debug_dump("[%d] try to lock\n", tid); | 
|---|
| 345 | for (;;) { | 
|---|
| 346 | // returns true if the comparison is successful and newval was written | 
|---|
| 347 | if (__sync_bool_compare_and_swap(&dumper->mutex, 1, 0)) { | 
|---|
| 348 | // debug_dump("[%d] lock ok\n", tid); | 
|---|
| 349 | break; | 
|---|
| 350 | } | 
|---|
| 351 |  | 
|---|
| 352 | /* Futex is not available; wait */ | 
|---|
| 353 | s = syscall_no_hook(SYS_futex, | 
|---|
| 354 | &dumper->mutex, FUTEX_WAIT, 0, NULL, NULL, 0); | 
|---|
| 355 | if (s == -1 && errno != EAGAIN) { | 
|---|
| 356 | // debug_dump("[%d] FUTEX_WAIT failed, errno=%d\n", tid, errno); | 
|---|
| 357 | break; | 
|---|
| 358 | } | 
|---|
| 359 | // try again for there are more than one thread waited. | 
|---|
| 360 | } | 
|---|
| 361 | } | 
|---|
| 362 |  | 
|---|
| 363 | void unlock_buffers(MemoryDumper *dumper) | 
|---|
| 364 | { | 
|---|
| 365 | // int tid = syscall_no_hook(SYS_gettid); | 
|---|
| 366 | // debug_dump("[%d] try to unlock\n", tid); | 
|---|
| 367 | if (__sync_bool_compare_and_swap(&dumper->mutex, 0, 1)) { | 
|---|
| 368 | if (syscall_no_hook(SYS_futex, | 
|---|
| 369 | &dumper->mutex, FUTEX_WAKE, 1, NULL, NULL, 0) < 0) { | 
|---|
| 370 | // debug_dump("[%d] FUTEX_WAKE failed, errno=%d\n", tid, errno); | 
|---|
| 371 | } | 
|---|
| 372 | else { | 
|---|
| 373 | // debug_dump("[%d] unlock ok\n", tid); | 
|---|
| 374 | } | 
|---|
| 375 | } | 
|---|
| 376 | else { | 
|---|
| 377 | assert(0); | 
|---|
| 378 | } | 
|---|
| 379 | } | 
|---|
| 380 |  | 
|---|
| 381 | long get_sp(void) | 
|---|
| 382 | { | 
|---|
| 383 | long sp = 0; | 
|---|
| 384 | #if defined(__x86_64__) | 
|---|
| 385 | __asm__ volatile( "\t movq %%rsp, %0": | 
|---|
| 386 | "=r"(sp)); | 
|---|
| 387 | #elif defined(__sw_64) | 
|---|
| 388 | __asm__ volatile( "\t mov $30, %0": | 
|---|
| 389 | "=r"(sp)); | 
|---|
| 390 | #elif defined(__mips64) | 
|---|
| 391 | __asm__ volatile( "\t move %0, $sp": | 
|---|
| 392 | "=r"(sp)); | 
|---|
| 393 | #elif defined(__aarch64__) | 
|---|
| 394 | __asm__ volatile( "\t mov %0, sp": | 
|---|
| 395 | "=r"(sp)); | 
|---|
| 396 | #endif | 
|---|
| 397 | return sp; | 
|---|
| 398 | } | 
|---|
| 399 |  | 
|---|
| 400 | void get_regs(char* p) | 
|---|
| 401 | { | 
|---|
| 402 | #if defined(__x86_64__) | 
|---|
| 403 | __asm__ volatile( "\t movq %%rsp, %0": | 
|---|
| 404 | "=r"(((struct user_regs_struct *)(p))->rsp)); | 
|---|
| 405 | __asm__ volatile( "\t movq %%rbp, %0": | 
|---|
| 406 | "=r"(((struct user_regs_struct *)(p))->rbp)); | 
|---|
| 407 | __asm__ volatile( "\t lea (%%rip), %%rax\n\t movq %%rax,%0": | 
|---|
| 408 | "=r"(((struct user_regs_struct *)(p))->rip)); | 
|---|
| 409 | #elif defined(__sw_64) | 
|---|
| 410 | __asm__ volatile( "\t mov $26, %0": | 
|---|
| 411 | "=r"(((USER_REGS *)(p))->ra)); | 
|---|
| 412 | __asm__ volatile( "\t mov $30, %0": | 
|---|
| 413 | "=r"(((USER_REGS *)(p))->sp)); | 
|---|
| 414 | #elif defined(__mips64) | 
|---|
| 415 | __asm__ volatile( "\t move %0, $ra": | 
|---|
| 416 | "=r"(((USER_REGS *)(p))->ra)); | 
|---|
| 417 | __asm__ volatile( "\t move %0, $sp": | 
|---|
| 418 | "=r"(((USER_REGS *)(p))->sp)); | 
|---|
| 419 | #elif defined(__aarch64__) | 
|---|
| 420 | __asm__ volatile( "\t mov %0, x30": | 
|---|
| 421 | "=r"(((USER_REGS *)(p))->ra)); | 
|---|
| 422 | __asm__ volatile( "\t mov %0, sp": | 
|---|
| 423 | "=r"(((USER_REGS *)(p))->sp)); | 
|---|
| 424 | #endif | 
|---|
| 425 | } | 
|---|
| 426 |  | 
|---|
| 427 | void record_event_simple(MemoryDumper* dumper, int type, const char* msg, int len) | 
|---|
| 428 | { | 
|---|
| 429 | int tid = syscall_no_hook(SYS_gettid); | 
|---|
| 430 | int msg_len = len > (int)(sizeof(long)) ? len:0; | 
|---|
| 431 | char *sp = NULL; | 
|---|
| 432 | char *sp_end = NULL; | 
|---|
| 433 | int stack_size = 0; | 
|---|
| 434 | char *walk = NULL; | 
|---|
| 435 | int current = 0; | 
|---|
| 436 | const int cpu_size = sizeof(USER_REGS) + sizeof(USER_FPREGS); | 
|---|
| 437 |  | 
|---|
| 438 | lock_buffers(dumper); | 
|---|
| 439 |  | 
|---|
| 440 | assert(msg_len <= EVENT_EXTRA_INFO_SIZE); | 
|---|
| 441 | assert(DUMP_REASON_signal < type && type < DUMP_REASON_ptrace); | 
|---|
| 442 |  | 
|---|
| 443 | current = dumper->current; | 
|---|
| 444 |  | 
|---|
| 445 | // check stack range | 
|---|
| 446 | sp = (char *)(get_sp() & (~(dumper->page_size - 1))); | 
|---|
| 447 | if ((uintptr_t)sp < dumper->stack_begin || | 
|---|
| 448 | (uintptr_t)sp > dumper->stack_end) { | 
|---|
| 449 | stack_size = get_stack_range(dumper, (uintptr_t)sp); | 
|---|
| 450 | sp_end = sp + stack_size; | 
|---|
| 451 | } else { | 
|---|
| 452 | sp_end = sp + dumper->max_stack_size; | 
|---|
| 453 | if (sp_end > (char *)(dumper->stack_end)) { | 
|---|
| 454 | sp_end = (char *)(dumper->stack_end); | 
|---|
| 455 | } | 
|---|
| 456 | if (sp_end > sp) stack_size = (sp_end - sp); | 
|---|
| 457 | else stack_size = 0; | 
|---|
| 458 | } | 
|---|
| 459 |  | 
|---|
| 460 | if (current + stack_size + cpu_size + msg_len + SAFE_GUARD > dumper->size) { | 
|---|
| 461 | send_cmd(SYS_flush_buffers, current); | 
|---|
| 462 | current = 0; | 
|---|
| 463 | } | 
|---|
| 464 | debug_dump( "dump-event begin:tid=%d,type=%d;pos=%d;\n", | 
|---|
| 465 | tid, type, current); | 
|---|
| 466 |  | 
|---|
| 467 | walk = BUFFER_HEAD(dumper) + current; | 
|---|
| 468 | syscall_no_hook(SYS_clock_gettime, CLOCK_REALTIME, (struct timespec *)walk); | 
|---|
| 469 | walk += sizeof(struct timespec); | 
|---|
| 470 |  | 
|---|
| 471 | *(short *)walk = type; | 
|---|
| 472 | *(short *)(walk + 2) = 1; | 
|---|
| 473 | walk += sizeof(int); | 
|---|
| 474 |  | 
|---|
| 475 | *(int *)walk = tid | (msg_len<<16); // extra data is msg | 
|---|
| 476 | walk += sizeof(int); | 
|---|
| 477 | memcpy(walk, msg, msg_len); | 
|---|
| 478 | walk += msg_len; | 
|---|
| 479 |  | 
|---|
| 480 | *(short *)walk = tid; | 
|---|
| 481 | *(short *)(walk + 2) = cpu_size; | 
|---|
| 482 | walk += sizeof(int); | 
|---|
| 483 |  | 
|---|
| 484 | get_regs(walk); | 
|---|
| 485 | walk += cpu_size; | 
|---|
| 486 |  | 
|---|
| 487 | // TODO: dump TLS (.tbss + .tdata, _tls_get_addr) | 
|---|
| 488 | *(int *)walk = 0; | 
|---|
| 489 | walk += sizeof(int); | 
|---|
| 490 |  | 
|---|
| 491 | // stack is always zero | 
|---|
| 492 | *(char **)walk = sp; | 
|---|
| 493 | *(int *)(walk + sizeof(sp)) = stack_size; | 
|---|
| 494 | walk += sizeof(char *) + sizeof(int); | 
|---|
| 495 | if (stack_size > 0) { | 
|---|
| 496 | memcpy(walk, sp, stack_size); | 
|---|
| 497 | walk += stack_size; | 
|---|
| 498 | } | 
|---|
| 499 |  | 
|---|
| 500 | *(int *)walk = 0; // heap count always zero; | 
|---|
| 501 | walk += sizeof(int); | 
|---|
| 502 |  | 
|---|
| 503 | dumper->current = walk - BUFFER_HEAD(dumper); | 
|---|
| 504 | assert(dumper->current < dumper->size); | 
|---|
| 505 |  | 
|---|
| 506 | debug_dump( "dump-event end:tid=%d,type=%d;pos=%d;\n", | 
|---|
| 507 | tid, type, dumper->current); | 
|---|
| 508 | unlock_buffers(dumper); | 
|---|
| 509 | } | 
|---|
| 510 |  | 
|---|
| 511 | typedef struct tagVmSegment { | 
|---|
| 512 | uintptr_t start; | 
|---|
| 513 | uintptr_t end; | 
|---|
| 514 | }VmSegment; | 
|---|
| 515 |  | 
|---|
| 516 | static char *get_line(char *buf, int size) | 
|---|
| 517 | { | 
|---|
| 518 | for (int i = 0; i < size; ++i) { | 
|---|
| 519 | if (buf[i] == '\n') { | 
|---|
| 520 | return buf + i; | 
|---|
| 521 | } | 
|---|
| 522 | } | 
|---|
| 523 |  | 
|---|
| 524 | return NULL; | 
|---|
| 525 | } | 
|---|
| 526 |  | 
|---|
| 527 | static int get_stack_range(MemoryDumper *dumper, uintptr_t sp) | 
|---|
| 528 | { | 
|---|
| 529 | char buf[4096]; | 
|---|
| 530 | char *line = NULL; | 
|---|
| 531 | char *line_end = NULL; | 
|---|
| 532 | char *stop = NULL; | 
|---|
| 533 | uintptr_t start = 0; | 
|---|
| 534 | uintptr_t end = 0; | 
|---|
| 535 | int eof = 0; | 
|---|
| 536 | int size = 256; | 
|---|
| 537 | int remain = 0; | 
|---|
| 538 |  | 
|---|
| 539 | #if defined(__aarch64__) | 
|---|
| 540 | int fd = syscall_no_hook(SYS_openat, AT_FDCWD, "/proc/self/maps", O_RDONLY); | 
|---|
| 541 | #else | 
|---|
| 542 | int fd = syscall_no_hook(SYS_open, "/proc/self/maps", O_RDONLY); | 
|---|
| 543 | #endif | 
|---|
| 544 | if (fd <= 0) { | 
|---|
| 545 | return 256; | 
|---|
| 546 | } | 
|---|
| 547 |  | 
|---|
| 548 | while (!eof) { | 
|---|
| 549 | int bytes = syscall_no_hook(SYS_read, | 
|---|
| 550 | fd, buf + remain, sizeof(buf) - remain); | 
|---|
| 551 | if (bytes <= 0) { | 
|---|
| 552 | eof = 1; | 
|---|
| 553 | break; | 
|---|
| 554 | } | 
|---|
| 555 | remain += bytes; | 
|---|
| 556 |  | 
|---|
| 557 | line = buf; | 
|---|
| 558 |  | 
|---|
| 559 | while (remain > 0) { | 
|---|
| 560 | line_end = get_line(line, remain); | 
|---|
| 561 | if (NULL == line_end) { | 
|---|
| 562 | memmove(buf, line, remain); | 
|---|
| 563 | break; | 
|---|
| 564 | } | 
|---|
| 565 | start = strtol(line, &stop, 16); | 
|---|
| 566 | end = strtol(stop + 1, &stop, 16); | 
|---|
| 567 | if (start <= sp && sp <= end) { | 
|---|
| 568 | eof = 1; | 
|---|
| 569 | size = end - sp; | 
|---|
| 570 | break; | 
|---|
| 571 | } | 
|---|
| 572 | remain -= (line_end + 1 - line); | 
|---|
| 573 | line = line_end + 1; | 
|---|
| 574 | } | 
|---|
| 575 | } | 
|---|
| 576 | syscall_no_hook(SYS_close, fd); | 
|---|
| 577 |  | 
|---|
| 578 | if (size > dumper->max_stack_size) { | 
|---|
| 579 | size = dumper->max_stack_size; | 
|---|
| 580 | } | 
|---|
| 581 |  | 
|---|
| 582 | return size; | 
|---|
| 583 | } | 
|---|
| 584 |  | 
|---|
| 585 | static int my_strlen(char* buf, int max) | 
|---|
| 586 | { | 
|---|
| 587 | int i = 0; | 
|---|
| 588 | while (buf[i] > 0 && i < max) { | 
|---|
| 589 | ++i; | 
|---|
| 590 | } | 
|---|
| 591 |  | 
|---|
| 592 | if (0 == buf[i]) { | 
|---|
| 593 | ++i; | 
|---|
| 594 | } | 
|---|
| 595 |  | 
|---|
| 596 | return i; | 
|---|
| 597 | } | 
|---|
| 598 |  | 
|---|
| 599 | /* | 
|---|
| 600 | * binary layout for one event, always 4bytes align: | 
|---|
| 601 | * time(sizeof(struct timespec)), event_type(2byte), thread_num(2byte), | 
|---|
| 602 | * current_tid(2byte), event_extra_size(2byte)[,event_extra_data] | 
|---|
| 603 | * { | 
|---|
| 604 | *   tid(2byte), cpu_context_size(2byte)[, user_regs_struct+user_fpregs_struct], | 
|---|
| 605 | *    tls_size(4byte)[, tls_data] | 
|---|
| 606 | *    stack_addr(uintptr_t), stack_length(4byte)[,stack_data] | 
|---|
| 607 | * } | 
|---|
| 608 | * | 
|---|
| 609 | * ... | 
|---|
| 610 | * | 
|---|
| 611 | * { | 
|---|
| 612 | *   tid(2byte), cpu_context_size(2byte)[, user_regs_struct+user_fpregs_struct], | 
|---|
| 613 | *    tls_size(4byte)[, tls_data] | 
|---|
| 614 | *    stack_addr(uintptr_t), stack_length(4byte)[,stack_data] | 
|---|
| 615 | * } | 
|---|
| 616 | * heap_count(4byte)[, addr(uintptr_t)+size(4byte)+heap_data, ...] | 
|---|
| 617 | */ | 
|---|
| 618 | void record_syscall(MemoryDumper *dumper, int nr, long* args, long result, void *cpu) | 
|---|
| 619 | { | 
|---|
| 620 | VmSegment segs[12]; | 
|---|
| 621 | VmSegment *seg = &segs[0]; | 
|---|
| 622 | int heap_count = 0; | 
|---|
| 623 | int heap_size = 0; | 
|---|
| 624 | unsigned char arg_flag = 0; | 
|---|
| 625 | unsigned char arg_count = 0; | 
|---|
| 626 | char *sp = NULL; | 
|---|
| 627 | char *sp_end = NULL; | 
|---|
| 628 | int stack_size = 0; | 
|---|
| 629 | char *walk = NULL; | 
|---|
| 630 | const int cpu_size = sizeof(USER_REGS) + sizeof(USER_FPREGS); | 
|---|
| 631 | int current = 0; | 
|---|
| 632 | int syscall_parameter_size = 0; | 
|---|
| 633 | const uintptr_t mask = ~3; | 
|---|
| 634 | USER_REGS *ctx = (USER_REGS*)cpu; | 
|---|
| 635 | int tid = syscall_no_hook(SYS_gettid); | 
|---|
| 636 |  | 
|---|
| 637 | lock_buffers(dumper); | 
|---|
| 638 |  | 
|---|
| 639 | // the follow memcpy maybe crashed so store current position first! | 
|---|
| 640 | current = dumper->current; | 
|---|
| 641 | syscall_parameter_size = dumper->max_param_size; | 
|---|
| 642 |  | 
|---|
| 643 | // check stack range | 
|---|
| 644 | #if defined(__x86_64__) | 
|---|
| 645 | sp = (char *)(ctx->rsp & (~(dumper->page_size - 1))); | 
|---|
| 646 | #else | 
|---|
| 647 | sp = (char *)(ctx->sp & (~(dumper->page_size - 1))); | 
|---|
| 648 | #endif | 
|---|
| 649 | if ((uintptr_t)sp < dumper->stack_begin || | 
|---|
| 650 | (uintptr_t)sp > dumper->stack_end) { | 
|---|
| 651 | stack_size = get_stack_range(dumper, (uintptr_t)sp); | 
|---|
| 652 | sp_end = sp + stack_size; | 
|---|
| 653 | } else { | 
|---|
| 654 | sp_end = sp + dumper->max_stack_size; | 
|---|
| 655 | if (sp_end > (char *)(dumper->stack_end)) { | 
|---|
| 656 | sp_end = (char *)(dumper->stack_end); | 
|---|
| 657 | } | 
|---|
| 658 | if (sp_end > sp) stack_size = (sp_end - sp); | 
|---|
| 659 | else stack_size = 0; | 
|---|
| 660 | } | 
|---|
| 661 |  | 
|---|
| 662 | // check if dump parameter of syscall | 
|---|
| 663 | arg_flag = parameter_flags[2 * (nr -  __NR_Linux)]; | 
|---|
| 664 | arg_count = parameter_flags[2 * (nr - __NR_Linux) + 1]; | 
|---|
| 665 | if ((arg_flag != 0) && (syscall_parameter_size > 0)) { | 
|---|
| 666 | // parameter is void*, size indicate in next parameter | 
|---|
| 667 | uint8_t size_indicate_by_next = arg_flag & 0x80; | 
|---|
| 668 | for (unsigned char i = 0; i < arg_count; ++i) { | 
|---|
| 669 | if (arg_flag & 1) { | 
|---|
| 670 | uintptr_t addr = args[i]; | 
|---|
| 671 | if ((addr > 0) && | 
|---|
| 672 | ((addr < (uintptr_t)sp) || | 
|---|
| 673 | (addr > (uintptr_t)sp_end))) { | 
|---|
| 674 | seg->start = addr; | 
|---|
| 675 | if (size_indicate_by_next) { | 
|---|
| 676 | if (args[i + 1] > 0) { | 
|---|
| 677 | seg->end = addr + args[i + 1]; | 
|---|
| 678 | ++seg; | 
|---|
| 679 | } | 
|---|
| 680 | break; | 
|---|
| 681 | } | 
|---|
| 682 | seg->end = addr + my_strlen((char *)addr, syscall_parameter_size); | 
|---|
| 683 | ++seg; | 
|---|
| 684 | } | 
|---|
| 685 | } | 
|---|
| 686 | arg_flag >>= 1; | 
|---|
| 687 | } | 
|---|
| 688 |  | 
|---|
| 689 | if (seg != &segs[0]) { | 
|---|
| 690 | heap_count = seg - &segs[0]; | 
|---|
| 691 | seg = &segs[0]; | 
|---|
| 692 | for (int i = 0; i < heap_count; ++i) { | 
|---|
| 693 | // make size is 4byte align | 
|---|
| 694 | heap_size += ((seg[i].end - seg[i].start + 3) & mask) + | 
|---|
| 695 | sizeof(int) + sizeof(uintptr_t); | 
|---|
| 696 | } | 
|---|
| 697 | } | 
|---|
| 698 | } | 
|---|
| 699 |  | 
|---|
| 700 | if (current + stack_size + cpu_size + heap_size + SAFE_GUARD > dumper->size) { | 
|---|
| 701 | send_cmd(SYS_flush_buffers, current); | 
|---|
| 702 | current = 0; | 
|---|
| 703 | } | 
|---|
| 704 | debug_dump( "dump-syscall begin:tid=%d,syscall=%d,%lx;pos=%d;stack=%p,%d;\n", | 
|---|
| 705 | tid, nr, result, current, sp, stack_size); | 
|---|
| 706 |  | 
|---|
| 707 | walk = BUFFER_HEAD(dumper) + current; | 
|---|
| 708 | syscall_no_hook(SYS_clock_gettime, CLOCK_REALTIME, (struct timespec *)walk); | 
|---|
| 709 | walk += sizeof(struct timespec); | 
|---|
| 710 |  | 
|---|
| 711 | *(short *)walk = nr; | 
|---|
| 712 | *(short *)(walk + 2) = 1; | 
|---|
| 713 | walk += sizeof(int); | 
|---|
| 714 |  | 
|---|
| 715 | *(int *)walk = tid | (sizeof(long)<<16); // extra data is syscall result | 
|---|
| 716 | walk += sizeof(int); | 
|---|
| 717 | *(long *)walk = result; | 
|---|
| 718 | walk += sizeof(long); | 
|---|
| 719 |  | 
|---|
| 720 | *(short *)walk = tid; | 
|---|
| 721 | *(short *)(walk + 2) = cpu_size; | 
|---|
| 722 | walk += sizeof(int); | 
|---|
| 723 |  | 
|---|
| 724 | memcpy(walk, ctx, sizeof(USER_REGS)); | 
|---|
| 725 | #if defined(__mips64) || defined(__sw_64) | 
|---|
| 726 | ((USER_REGS*)walk)->sp += 512; //see syscall_wrapper in mips64/sunway64 | 
|---|
| 727 | ((USER_REGS*)walk)->ra = ctx->t2; //see syscall_wrapper in mips64/sunway64 | 
|---|
| 728 | ((USER_REGS*)walk)->pc = ctx->t3; // see build_trampoline in mips64/sunway64 | 
|---|
| 729 | #elif defined(__aarch64__) | 
|---|
| 730 | ((USER_REGS*)walk)->sp += 512; //see syscall_wrapper in arm64 | 
|---|
| 731 | ((USER_REGS*)walk)->ra = ctx->x11; //see syscall_wrapper in arm64 | 
|---|
| 732 | ((USER_REGS*)walk)->pc = ctx->x10; // see build_trampoline in arm64 | 
|---|
| 733 | #endif | 
|---|
| 734 | walk += cpu_size; | 
|---|
| 735 |  | 
|---|
| 736 | // TODO: dump TLS (.tbss + .tdata, _tls_get_addr) | 
|---|
| 737 | *(int *)walk = 0; | 
|---|
| 738 | walk += sizeof(int); | 
|---|
| 739 |  | 
|---|
| 740 | // stack | 
|---|
| 741 | *(char **)walk = sp; | 
|---|
| 742 | *(int *)(walk + sizeof(sp)) = stack_size; | 
|---|
| 743 | walk += sizeof(char *) + sizeof(int); | 
|---|
| 744 |  | 
|---|
| 745 | if (stack_size > 0) { | 
|---|
| 746 | memcpy(walk, sp, stack_size); | 
|---|
| 747 | walk += stack_size; | 
|---|
| 748 | } | 
|---|
| 749 |  | 
|---|
| 750 | *(int *)walk = heap_count; | 
|---|
| 751 | walk += sizeof(int); | 
|---|
| 752 | for (int i = 0; i < heap_count; ++i) { | 
|---|
| 753 | int size = seg->end - seg->start; | 
|---|
| 754 | int align = ((size + 3) & mask) - size; | 
|---|
| 755 | *(uintptr_t *)walk = seg->start; | 
|---|
| 756 | *(int *)(walk + sizeof(uintptr_t)) = size + align; | 
|---|
| 757 | memcpy(walk + sizeof(uintptr_t) + sizeof(int), | 
|---|
| 758 | (void *)seg->start, size); | 
|---|
| 759 | walk += size + sizeof(int) + sizeof(uintptr_t); | 
|---|
| 760 | if (align > 0) { | 
|---|
| 761 | memset(walk, 0xcc, align); | 
|---|
| 762 | walk += align; | 
|---|
| 763 | } | 
|---|
| 764 | ++seg; | 
|---|
| 765 | } | 
|---|
| 766 |  | 
|---|
| 767 | dumper->current = walk - BUFFER_HEAD(dumper); | 
|---|
| 768 | assert(dumper->current < dumper->size); | 
|---|
| 769 | ++dumper->syscall_count; | 
|---|
| 770 |  | 
|---|
| 771 | debug_dump( "dump-syscall end:tid=%d,syscall=%d;pos=%d;heap_count=%d;\n", | 
|---|
| 772 | tid, nr, dumper->current, heap_count); | 
|---|
| 773 |  | 
|---|
| 774 | unlock_buffers(dumper); | 
|---|
| 775 | } | 
|---|
| 776 |  | 
|---|
| 777 | static __attribute__((constructor)) void init_func(int argc, char **argv) | 
|---|
| 778 | { | 
|---|
| 779 | (void)argc; | 
|---|
| 780 |  | 
|---|
| 781 | if (!strcmp(argv[0], "gdb")) { | 
|---|
| 782 | return; | 
|---|
| 783 | } | 
|---|
| 784 |  | 
|---|
| 785 | #if defined(__sw_64) | 
|---|
| 786 | g_pid = syscall_no_hook(SYS_getxpid); | 
|---|
| 787 | #else | 
|---|
| 788 | g_pid = syscall_no_hook(SYS_getpid); | 
|---|
| 789 | #endif | 
|---|
| 790 |  | 
|---|
| 791 | if (getenv( "ST2_DEBUG_DUMP")) { | 
|---|
| 792 | debug_dumps_on = 1; | 
|---|
| 793 | debug_dump( "libinprocdump start g_pid=%d\n", g_pid); | 
|---|
| 794 | } | 
|---|
| 795 | } | 
|---|
| 796 |  | 
|---|