1// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5/*
6build:
7gcc -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
47static int g_pid = 0; // store current pid to avoid fork affect
48static MemoryDumper *g_dumper = NULL;
49static unsigned char *parameter_flags = NULL;
50struct number_set trace_set[1];
51static int get_stack_range(MemoryDumper *dumper, uintptr_t sp);
52
53long syscall_no_hook(long syscall_number, ...);
54
55static char debug_dumps_on = 0;
56
57void 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
86static 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
111static 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
137static 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
160static 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
188static 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
194unsigned 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
203static 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
230done:
231 return rc;
232}
233
234int 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
259MemoryDumper *get_memory_dumper(void)
260{
261 return g_dumper;
262}
263
264MemoryDumper *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
340void 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
363void 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
381long 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
400void 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
427void 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
511typedef struct tagVmSegment {
512 uintptr_t start;
513 uintptr_t end;
514}VmSegment;
515
516static 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
527static 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
585static 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 */
618void 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
777static __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