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