| 1 | // SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. |
| 2 | // |
| 3 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 4 | |
| 5 | #include <assert.h> |
| 6 | #include <fnmatch.h> |
| 7 | #include <limits.h> |
| 8 | #include <linux/net.h> |
| 9 | #include <sys/socket.h> |
| 10 | #include <sys/types.h> |
| 11 | #include <sys/ptrace.h> |
| 12 | #include <sys/syscall.h> |
| 13 | #include <sys/mman.h> |
| 14 | #include <sys/wait.h> |
| 15 | #include <sys/un.h> |
| 16 | #include <sys/user.h> |
| 17 | |
| 18 | #if !defined(__sw_64) && !defined(__aarch64__) |
| 19 | #include <capstone/capstone.h> |
| 20 | #endif |
| 21 | |
| 22 | #if defined(__i386__) || defined(__x86_64__) |
| 23 | #include <libunwind-ptrace.h> |
| 24 | #endif |
| 25 | |
| 26 | #include <string> |
| 27 | #include <memory> |
| 28 | |
| 29 | #include "cpu.h" |
| 30 | #include "debug.h" |
| 31 | #include "elf_helper.h" |
| 32 | |
| 33 | using namespace std; |
| 34 | |
| 35 | int write_mem(int pid, uintptr_t addr, const void* buf, int buf_size) |
| 36 | { |
| 37 | int nwritten = 0; |
| 38 | // ptrace operates on the word size of the host, so we really do want |
| 39 | // to use sizes of host types here. |
| 40 | uintptr_t word_size = sizeof(long); |
| 41 | |
| 42 | errno = 0; |
| 43 | |
| 44 | // Only write aligned words. This ensures we can always write the last |
| 45 | // byte before an unmapped region. |
| 46 | while (nwritten < buf_size) { |
| 47 | uintptr_t start = addr + nwritten; |
| 48 | uintptr_t start_word = start & ~(word_size - 1); |
| 49 | uintptr_t end_word = start_word + word_size; |
| 50 | uintptr_t length = min(end_word - start, uintptr_t(buf_size - nwritten)); |
| 51 | |
| 52 | long v; |
| 53 | if (length < word_size) { |
| 54 | v = ptrace(PTRACE_PEEKDATA, pid, start_word, nullptr); |
| 55 | if (errno) { |
| 56 | break; |
| 57 | } |
| 58 | } |
| 59 | |
| 60 | memcpy(reinterpret_cast<uint8_t*>(&v) + (start - start_word), |
| 61 | static_cast<const uint8_t*>(buf) + nwritten, length); |
| 62 | ptrace(PTRACE_POKEDATA, pid, start_word, reinterpret_cast<void*>(v)); |
| 63 | nwritten += length; |
| 64 | } |
| 65 | |
| 66 | return nwritten; |
| 67 | } |
| 68 | |
| 69 | int read_mem(int tid, uintptr_t addr, void* buf, int buf_size) |
| 70 | { |
| 71 | int nread = 0; |
| 72 | // ptrace operates on the word size of the host, so we really do want |
| 73 | // to use sizes of host types here. |
| 74 | uintptr_t word_size = sizeof(long); |
| 75 | // Only read aligned words. This ensures we can always read the last |
| 76 | // byte before an unmapped region. |
| 77 | uintptr_t start_word = addr & ~(word_size - 1); |
| 78 | uintptr_t end_word = (addr + buf_size) & ~(word_size - 1); |
| 79 | |
| 80 | errno = 0; |
| 81 | if (start_word < addr) { |
| 82 | // the first unaligned part |
| 83 | long v = ptrace(PTRACE_PEEKDATA, tid, start_word, nullptr); |
| 84 | long offset = (addr - start_word); |
| 85 | start_word += word_size; |
| 86 | nread = start_word - addr; |
| 87 | if (nread > buf_size) nread = buf_size; |
| 88 | memcpy(static_cast<uint8_t*>(buf), |
| 89 | reinterpret_cast<uint8_t*>(&v) + offset, nread); |
| 90 | } |
| 91 | |
| 92 | while (start_word < end_word) { |
| 93 | long v = ptrace(PTRACE_PEEKDATA, tid, start_word, nullptr); |
| 94 | if (errno) { |
| 95 | break; |
| 96 | } |
| 97 | start_word += word_size; |
| 98 | memcpy(static_cast<uint8_t*>(buf) + nread, |
| 99 | reinterpret_cast<uint8_t*>(&v), word_size); |
| 100 | nread += word_size; |
| 101 | } |
| 102 | |
| 103 | if (nread < buf_size) { |
| 104 | // the last unaligned part |
| 105 | long v = ptrace(PTRACE_PEEKDATA, tid, start_word, nullptr); |
| 106 | memcpy(static_cast<uint8_t*>(buf) + nread, |
| 107 | reinterpret_cast<uint8_t*>(&v), buf_size - nread); |
| 108 | nread = buf_size; |
| 109 | } |
| 110 | |
| 111 | return nread; |
| 112 | } |
| 113 | |
| 114 | int gdb_disass(int pid, void* base, int len) |
| 115 | { |
| 116 | #if defined(__sw_64) || defined(__aarch64__) |
| 117 | return -1; |
| 118 | #else |
| 119 | csh handle; |
| 120 | cs_insn* insn = NULL; |
| 121 | uintptr_t address = (uintptr_t)base; |
| 122 | string codestr('0', len); |
| 123 | uint8_t* code = (uint8_t*)codestr.data(); |
| 124 | size_t code_size = len; |
| 125 | |
| 126 | int got = read_mem(pid, address, code, code_size); |
| 127 | if (got < (int)code_size) { |
| 128 | return -1; |
| 129 | } |
| 130 | |
| 131 | #if defined(__x86_64__) |
| 132 | if (cs_open(CS_ARCH_X86, cs_mode(CS_MODE_64), &handle) != CS_ERR_OK) { |
| 133 | #elif defined(__mips64) |
| 134 | if (cs_open(CS_ARCH_MIPS, cs_mode(CS_MODE_MIPS64|CS_MODE_LITTLE_ENDIAN), |
| 135 | &handle) != CS_ERR_OK) { |
| 136 | #else |
| 137 | if (1) { |
| 138 | #endif |
| 139 | return -2; |
| 140 | } |
| 141 | |
| 142 | // allocate memory cache for 1 instruction, to be used by cs_disasm_iter later. |
| 143 | insn = cs_malloc(handle); |
| 144 | |
| 145 | /*Some mips struction on loonson not support by libcapstone |
| 146 | use this statement maybe better decode*/ |
| 147 | cs_option(handle, CS_OPT_SKIPDATA, CS_OPT_ON); |
| 148 | |
| 149 | // disassemble one instruction a time & store the result into @insn variable above |
| 150 | while(cs_disasm_iter(handle, (const uint8_t**)&code, &code_size, &address, insn)) |
| 151 | { |
| 152 | // analyze disassembled instruction in @insn variable ... |
| 153 | // NOTE: @code, @code_size & @address variables are all updated |
| 154 | // to point to the next instruction after each iteration. |
| 155 | |
| 156 | printf("0x%" PRIx64 ":\t%s\t%s\n" , |
| 157 | insn->address, insn->mnemonic, insn->op_str); |
| 158 | |
| 159 | // print implicit registers used by this instruction |
| 160 | cs_detail* detail = insn->detail; |
| 161 | if (!detail) continue; |
| 162 | |
| 163 | if (detail->regs_read_count > 0) { |
| 164 | printf("\tImplicit registers read: " ); |
| 165 | for (int n = 0; n < detail->regs_read_count; n++) { |
| 166 | printf("%s " , cs_reg_name(handle, detail->regs_read[n])); |
| 167 | } |
| 168 | printf("\n" ); |
| 169 | } |
| 170 | |
| 171 | // print implicit registers modified by this instruction |
| 172 | if (detail->regs_write_count > 0) { |
| 173 | printf("\tImplicit registers modified: " ); |
| 174 | for (int n = 0; n < detail->regs_write_count; n++) { |
| 175 | printf("%s " , cs_reg_name(handle, detail->regs_write[n])); |
| 176 | } |
| 177 | printf("\n" ); |
| 178 | } |
| 179 | |
| 180 | // print the groups this instruction belong to |
| 181 | if (detail->groups_count > 0) { |
| 182 | printf("\tThis instruction belongs to groups: " ); |
| 183 | for (int n = 0; n < detail->groups_count; n++) { |
| 184 | printf("%s " , cs_group_name(handle, detail->groups[n])); |
| 185 | } |
| 186 | printf("\n" ); |
| 187 | } |
| 188 | } |
| 189 | |
| 190 | // release the cache memory when done |
| 191 | cs_free(insn, 1); |
| 192 | cs_close(&handle); |
| 193 | return 0; |
| 194 | #endif |
| 195 | } |
| 196 | |
| 197 | int gdb_pause(pid_t pid) |
| 198 | { |
| 199 | int ret = ptrace(PTRACE_INTERRUPT, pid, nullptr, nullptr); |
| 200 | if (ret < 0) { |
| 201 | return -errno; |
| 202 | } |
| 203 | |
| 204 | return 0; |
| 205 | } |
| 206 | |
| 207 | // used by gdb console to debug dead-lock in recording |
| 208 | int gdb_cont(pid_t pid) |
| 209 | { |
| 210 | int ret = ptrace(PTRACE_CONT, pid, nullptr, nullptr); |
| 211 | if (ret < 0) { |
| 212 | return -errno; |
| 213 | } |
| 214 | |
| 215 | return 0; |
| 216 | } |
| 217 | |
| 218 | int gdb_step(int pid) |
| 219 | { |
| 220 | int ret = ptrace(PTRACE_SINGLESTEP, pid, nullptr, nullptr); |
| 221 | if (ret < 0) { |
| 222 | return -errno; |
| 223 | } |
| 224 | |
| 225 | return 0; |
| 226 | } |
| 227 | |
| 228 | int gdb_bt(int tid) |
| 229 | { |
| 230 | #if defined(__i386__) || defined(__x86_64__) |
| 231 | #define MAX_STACK_FRAME 64 |
| 232 | |
| 233 | unw_addr_space_t unwind_as; |
| 234 | struct UPT_info* ui = nullptr; |
| 235 | int n = 0, ret; |
| 236 | unw_cursor_t cursor; |
| 237 | unw_proc_info_t pi; |
| 238 | unw_word_t ip, sp, start_ip = 0, off; |
| 239 | char buf[512]; |
| 240 | |
| 241 | unwind_as = unw_create_addr_space (&_UPT_accessors, 0); |
| 242 | if (!unwind_as) { |
| 243 | printf("libunwind.unw_create_addr_space() failed\n" ); |
| 244 | return -1; |
| 245 | } |
| 246 | |
| 247 | ui = reinterpret_cast<struct UPT_info*>(_UPT_create(tid)); |
| 248 | ret = unw_init_remote (&cursor, unwind_as, ui); |
| 249 | if (ret < 0) { |
| 250 | printf("unw_init_remote() failed: pause first then try again, ret=%d\n" , ret); |
| 251 | _UPT_destroy (ui); |
| 252 | return -2; |
| 253 | } |
| 254 | |
| 255 | do { |
| 256 | if ((ret = unw_get_reg (&cursor, UNW_REG_IP, &ip)) < 0 || |
| 257 | (ret = unw_get_reg (&cursor, UNW_REG_SP, &sp)) < 0) { |
| 258 | printf("unw_get_reg failed: ret=%d\n" , ret); |
| 259 | break; |
| 260 | } |
| 261 | |
| 262 | if (n == 0) start_ip = ip; |
| 263 | |
| 264 | buf[0] = '\0'; |
| 265 | unw_get_proc_name (&cursor, buf, sizeof (buf), &off); |
| 266 | |
| 267 | if ((ret = unw_get_proc_info (&cursor, &pi)) < 0) { |
| 268 | printf("unw_get_proc_info(%lx)failed: ret=%d\n" , ip, ret); |
| 269 | break; |
| 270 | } |
| 271 | printf("\t[%d \t]ip=%-16lx\tsp=%-16lx\t%s+%lx\n" , n, ip, sp, buf, off); |
| 272 | |
| 273 | ret = unw_step (&cursor); |
| 274 | if (ret < 0) { |
| 275 | unw_get_reg (&cursor, UNW_REG_IP, &ip); |
| 276 | printf("FAILURE: unw_step() returned %d, for ip=%lx\n" , ret, ip); |
| 277 | break; |
| 278 | } |
| 279 | |
| 280 | if (++n > MAX_STACK_FRAME) { |
| 281 | /* guard against bad unwind info in old libraries... */ |
| 282 | printf("too deeply nested---assuming bogus unwind (start ip=%lx)\n" , start_ip); |
| 283 | break; |
| 284 | } |
| 285 | } |
| 286 | while (ret > 0); |
| 287 | |
| 288 | _UPT_destroy (ui); |
| 289 | if (unwind_as) { |
| 290 | unw_destroy_addr_space (unwind_as); |
| 291 | unwind_as = nullptr; |
| 292 | } |
| 293 | #elif defined(__mips__) || defined(__sw_64) || defined(__aarch64__) |
| 294 | USER_REGS regs; |
| 295 | printf("\ndump register:\n" ); |
| 296 | gdb_reg(tid, ®s); |
| 297 | printf("\ndump memory of pc:\n" ); |
| 298 | gdb_mem(tid, (void*)regs.pc, 256, 1); |
| 299 | printf("\ndump memory of sp:\n" ); |
| 300 | gdb_mem(tid, (void*)regs.sp, 256, 1); |
| 301 | #else |
| 302 | printf("not implement in your arch\n" ); |
| 303 | #endif |
| 304 | |
| 305 | return 0; |
| 306 | } |
| 307 | |
| 308 | static unsigned int copy_print_char (char* out, const unsigned char* buf, unsigned int count) |
| 309 | { |
| 310 | unsigned int i = 0; |
| 311 | while (i<count) |
| 312 | { |
| 313 | if (buf[i] < 0x20 || buf[i] > 0x7f) |
| 314 | { |
| 315 | out[i] = '.'; |
| 316 | } |
| 317 | else |
| 318 | { |
| 319 | out[i] = buf[i]; |
| 320 | } |
| 321 | ++i; |
| 322 | } |
| 323 | out[i++] = '\n'; |
| 324 | |
| 325 | return i; |
| 326 | } |
| 327 | |
| 328 | static void print_bytes(const void* buffer, int total) |
| 329 | { |
| 330 | char* str = NULL; |
| 331 | int i = 0; |
| 332 | int j = 0; |
| 333 | unsigned char* buf = (unsigned char*)buffer; |
| 334 | int count = total; |
| 335 | unique_ptr<char[]> outbuf = make_unique<char[]>(count*4 + (count+15)/16*2 + 48); |
| 336 | |
| 337 | str = outbuf.get(); |
| 338 | |
| 339 | while ( i<count ) |
| 340 | { |
| 341 | j += sprintf(str+j, "%02x " , buf[i] ); |
| 342 | |
| 343 | ++i; |
| 344 | if ( !( i&15 ) ) |
| 345 | { |
| 346 | str[j++] = ' '; |
| 347 | j += copy_print_char (str+j, buf+i-16, 16); |
| 348 | } |
| 349 | } |
| 350 | |
| 351 | if ( count&15 ) |
| 352 | { |
| 353 | i = count&15; //left |
| 354 | memset (str+j, 0x20, (16-i)*3 + 1 ); |
| 355 | j += (16-i)*3 + 1; |
| 356 | j += copy_print_char (str+j, buf+count-i, i); |
| 357 | } |
| 358 | str[j] = 0; |
| 359 | |
| 360 | printf("%s" , str); |
| 361 | } |
| 362 | |
| 363 | static char hex2char(unsigned char hex) |
| 364 | { |
| 365 | if (hex<0xa) return hex+'0'; |
| 366 | else return hex-0xa+'a'; |
| 367 | } |
| 368 | |
| 369 | static void hex2str2(unsigned char* hex, int size, char* str) |
| 370 | { |
| 371 | int i = 0; |
| 372 | char* walk = str; |
| 373 | for (;i<size;) |
| 374 | { |
| 375 | walk[0] = hex2char(hex[2*i+1]>>4); |
| 376 | walk[1] = hex2char(hex[2*i+1]&0xf); |
| 377 | walk[2] = hex2char(hex[2*i]>>4); |
| 378 | walk[3] = hex2char(hex[2*i]&0xf); |
| 379 | walk[4] = 0x20; |
| 380 | ++i; |
| 381 | walk += 5; |
| 382 | if (!(i & 7)) { |
| 383 | *walk++ = '\n'; |
| 384 | } |
| 385 | } |
| 386 | |
| 387 | *walk = 0; |
| 388 | } |
| 389 | |
| 390 | static void hex2str4(unsigned char* hex, int size, char* str) |
| 391 | { |
| 392 | int i = 0; |
| 393 | char* walk = str; |
| 394 | for (;i<size;) |
| 395 | { |
| 396 | walk[0] = hex2char(hex[4*i+3]>>4); |
| 397 | walk[1] = hex2char(hex[4*i+3]&0xf); |
| 398 | walk[2] = hex2char(hex[4*i+2]>>4); |
| 399 | walk[3] = hex2char(hex[4*i+2]&0xf); |
| 400 | walk[4] = hex2char(hex[4*i+1]>>4); |
| 401 | walk[5] = hex2char(hex[4*i+1]&0xf); |
| 402 | walk[6] = hex2char(hex[4*i]>>4); |
| 403 | walk[7] = hex2char(hex[4*i]&0xf); |
| 404 | walk[8] = 0x20; |
| 405 | ++i; |
| 406 | walk += 9; |
| 407 | if (!(i&3)) { |
| 408 | *walk++ = '\n'; |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | *walk = 0; |
| 413 | } |
| 414 | |
| 415 | static void hex2str8(unsigned char* hex, int size, char* str) |
| 416 | { |
| 417 | int i = 0; |
| 418 | char* walk = str; |
| 419 | for (;i<size;) |
| 420 | { |
| 421 | walk[0 ] = hex2char(hex[8*i+7]>>4); |
| 422 | walk[1 ] = hex2char(hex[8*i+7]&0xf); |
| 423 | walk[2 ] = hex2char(hex[8*i+6]>>4); |
| 424 | walk[3 ] = hex2char(hex[8*i+6]&0xf); |
| 425 | walk[4 ] = hex2char(hex[8*i+5]>>4); |
| 426 | walk[5 ] = hex2char(hex[8*i+5]&0xf); |
| 427 | walk[6 ] = hex2char(hex[8*i+4]>>4); |
| 428 | walk[7 ] = hex2char(hex[8*i+4]&0xf); |
| 429 | walk[8 ] = hex2char(hex[8*i+3]>>4); |
| 430 | walk[9 ] = hex2char(hex[8*i+3]&0xf); |
| 431 | walk[10] = hex2char(hex[8*i+2]>>4); |
| 432 | walk[11] = hex2char(hex[8*i+2]&0xf); |
| 433 | walk[12] = hex2char(hex[8*i+1]>>4); |
| 434 | walk[13] = hex2char(hex[8*i+1]&0xf); |
| 435 | walk[14] = hex2char(hex[8*i]>>4); |
| 436 | walk[15] = hex2char(hex[8*i]&0xf); |
| 437 | walk[16] = 0x20; |
| 438 | ++i; |
| 439 | walk += 17; |
| 440 | if (!(i&1)) { |
| 441 | *walk++ = '\n'; |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | *walk = 0; |
| 446 | } |
| 447 | |
| 448 | static void print_short(const void* buffer, int total) |
| 449 | { |
| 450 | int count = total/sizeof(short); |
| 451 | unique_ptr<char[]> outbuf = make_unique<char[]>(count*5 + count/8 + 32); |
| 452 | hex2str2((unsigned char*)buffer, count, outbuf.get()); |
| 453 | printf("%s" , outbuf.get()); |
| 454 | } |
| 455 | |
| 456 | static void print_word(const void* buffer, int total) |
| 457 | { |
| 458 | int count = total/sizeof(int); |
| 459 | unique_ptr<char[]> outbuf = make_unique<char[]>(count*9 + count/4 + 32); |
| 460 | hex2str4((unsigned char*)buffer, count, outbuf.get()); |
| 461 | printf("%s" , outbuf.get()); |
| 462 | } |
| 463 | |
| 464 | static void print_dword(const void* buffer, int total) |
| 465 | { |
| 466 | int count = total/sizeof(long); |
| 467 | unique_ptr<char[]> outbuf = make_unique<char[]>(count*17 + count/2 + 32); |
| 468 | hex2str8((unsigned char*)buffer, count, outbuf.get()); |
| 469 | printf("%s" , outbuf.get()); |
| 470 | } |
| 471 | |
| 472 | int gdb_mem(int pid, void* base, int len, int group) |
| 473 | { |
| 474 | uintptr_t address = (uintptr_t)base; |
| 475 | unique_ptr<char[]> buffer = make_unique<char[]>(len); |
| 476 | void* code = buffer.get(); |
| 477 | int got = read_mem(pid, address, code, len); |
| 478 | if (got < 1) { |
| 479 | printf("failed to read memory from:%lx,%d\n" , address, len); |
| 480 | return -1; |
| 481 | } |
| 482 | |
| 483 | len = got; |
| 484 | switch(group) { |
| 485 | case 1: |
| 486 | print_bytes(code, len); |
| 487 | break; |
| 488 | case 2: |
| 489 | print_short(code, len); |
| 490 | break; |
| 491 | case 4: |
| 492 | print_word(code, len); |
| 493 | break; |
| 494 | case 8: |
| 495 | print_dword(code, len); |
| 496 | break; |
| 497 | default: |
| 498 | break; |
| 499 | } |
| 500 | |
| 501 | return len; |
| 502 | } |
| 503 | |
| 504 | int gdb_reg(int tid, USER_REGS* out) |
| 505 | { |
| 506 | USER_REGS regs; |
| 507 | |
| 508 | #if defined(__aarch64__) |
| 509 | static struct iovec io = { |
| 510 | .iov_base = ®s, |
| 511 | .iov_len = sizeof(regs) |
| 512 | }; |
| 513 | if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &io) < 0) { |
| 514 | #else |
| 515 | if (ptrace(PTRACE_GETREGS, tid, nullptr, ®s) < 0) { |
| 516 | #endif |
| 517 | return -errno; |
| 518 | } |
| 519 | |
| 520 | #if defined(__i386__) |
| 521 | #error unspport get_cpu_context |
| 522 | #elif defined(__x86_64__) |
| 523 | printf("\trip=%-16llx\n" , regs.rip); |
| 524 | printf("\trbp=%-16llx\n" , regs.rbp); |
| 525 | printf("\trsp=%-16llx\n" , regs.rsp); |
| 526 | |
| 527 | printf("\trax=%-16llx\n" , regs.rax); |
| 528 | printf("\trbx=%-16llx\n" , regs.rbx); |
| 529 | |
| 530 | printf("\trdi=%-16llx\n" , regs.rdi); |
| 531 | printf("\trsi=%-16llx\n" , regs.rsi); |
| 532 | printf("\trdx=%-16llx\n" , regs.rdx); |
| 533 | printf("\trcx=%-16llx\n" , regs.rcx); |
| 534 | printf("\tr8=%-16llx\n" , regs.r8); |
| 535 | printf("\tr9=%-16llx\n" , regs.r9); |
| 536 | |
| 537 | printf("\tr10=%-16llx\n" , regs.r10); |
| 538 | printf("\tr11=%-16llx\n" , regs.r11); |
| 539 | printf("\tr12=%-16llx\n" , regs.r12); |
| 540 | printf("\tr13=%-16llx\n" , regs.r13); |
| 541 | printf("\tr14=%-16llx\n" , regs.r14); |
| 542 | printf("\tr15=%-16llx\n" , regs.r15); |
| 543 | #elif defined(__mips__) || defined(__mips64) |
| 544 | printf("\tat=%-16lx\n" , regs.at); |
| 545 | printf("\tv0=%-16lx\n" , regs.v0); |
| 546 | printf("\tv1=%-16lx\n" , regs.v1); |
| 547 | printf("\ta0=%-16lx\n" , regs.a0); |
| 548 | printf("\ta1=%-16lx\n" , regs.a1); |
| 549 | printf("\ta2=%-16lx\n" , regs.a2); |
| 550 | printf("\ta3=%-16lx\n" , regs.a3); |
| 551 | |
| 552 | #if _MIPS_SIM != _ABIO32 |
| 553 | printf("\ta4=%-16lx\n" , regs.a4); |
| 554 | printf("\ta5=%-16lx\n" , regs.a5); |
| 555 | printf("\ta6=%-16lx\n" , regs.a6); |
| 556 | printf("\ta7=%-16lx\n" , regs.a7); |
| 557 | printf("\tt0=%-16lx\n" , regs.t0); |
| 558 | printf("\tt1=%-16lx\n" , regs.t1); |
| 559 | printf("\tt2=%-16lx\n" , regs.t2); |
| 560 | printf("\tt3=%-16lx\n" , regs.t3); |
| 561 | #else /* if _MIPS_SIM == _ABIO32 */ |
| 562 | printf("\tt0=%-16lx\n" , regs.t0); |
| 563 | printf("\tt1=%-16lx\n" , regs.t1); |
| 564 | printf("\tt2=%-16lx\n" , regs.t2); |
| 565 | printf("\tt3=%-16lx\n" , regs.t3); |
| 566 | printf("\tt4=%-16lx\n" , regs.t4); |
| 567 | printf("\tt5=%-16lx\n" , regs.t5); |
| 568 | printf("\tt6=%-16lx\n" , regs.t6); |
| 569 | printf("\tt7=%-16lx\n" , regs.t7); |
| 570 | #endif /* _MIPS_SIM == _ABIO32 */ |
| 571 | printf("\ts0=%-16lx\n" , regs.s0); |
| 572 | printf("\ts1=%-16lx\n" , regs.s1); |
| 573 | printf("\ts2=%-16lx\n" , regs.s2); |
| 574 | printf("\ts3=%-16lx\n" , regs.s3); |
| 575 | printf("\ts4=%-16lx\n" , regs.s4); |
| 576 | printf("\ts5=%-16lx\n" , regs.s5); |
| 577 | printf("\ts6=%-16lx\n" , regs.s6); |
| 578 | printf("\ts7=%-16lx\n" , regs.s7); |
| 579 | printf("\tt8=%-16lx\n" , regs.t8); |
| 580 | printf("\tt9=%-16lx\n" , regs.t9); |
| 581 | printf("\tk0=%-16lx\n" , regs.k0); |
| 582 | printf("\tk1=%-16lx\n" , regs.k1); |
| 583 | printf("\tgp=%-16lx\n" , regs.gp); |
| 584 | printf("\tsp=%-16lx\n" , regs.sp); |
| 585 | printf("\tfp=%-16lx\n" , regs.fp); |
| 586 | printf("\tra=%-16lx\n" , regs.ra); |
| 587 | |
| 588 | /* Saved special registers. */ |
| 589 | printf("\tlo=%-16lx\n" , regs.lo); |
| 590 | printf("\thi=%-16lx\n" , regs.hi); |
| 591 | printf("\tpc=%-16lx\n" , regs.pc); |
| 592 | printf("\tbadvaddr=%-16lx\n" , regs.cp0_badvaddr); |
| 593 | printf("\tstatus=%-16lx\n" , regs.cp0_status); |
| 594 | printf("\tcause=%-16lx\n" , regs.cp0_cause); |
| 595 | #elif defined(__sw_64) |
| 596 | printf("\tgp=%-16lx\n" , regs.gp); |
| 597 | printf("\tsp=%-16lx\n" , regs.sp); |
| 598 | printf("\tfp=%-16lx\n" , regs.s6); |
| 599 | printf("\tra=%-16lx\n" , regs.ra); |
| 600 | printf("\tpc=%-16lx\n" , regs.pc); |
| 601 | printf("\tt12=%-16lx\n" , regs.t12); |
| 602 | |
| 603 | printf("\tv0=%-16lx\n" , regs.v0); |
| 604 | printf("\ta0=%-16lx\n" , regs.a0); |
| 605 | printf("\ta1=%-16lx\n" , regs.a1); |
| 606 | printf("\ta2=%-16lx\n" , regs.a2); |
| 607 | printf("\ta3=%-16lx\n" , regs.a3); |
| 608 | printf("\ta4=%-16lx\n" , regs.a4); |
| 609 | printf("\ta5=%-16lx\n" , regs.a5); |
| 610 | #elif defined(__aarch64__) |
| 611 | printf("\tsp=%-16lx\n" , regs.sp); |
| 612 | printf("\tpc=%-16lx\n" , regs.pc); |
| 613 | |
| 614 | printf("\tx0=%-16lx\n" , regs.x0); |
| 615 | printf("\tx1=%-16lx\n" , regs.x1); |
| 616 | printf("\tx2=%-16lx\n" , regs.x2); |
| 617 | printf("\tx3=%-16lx\n" , regs.x3); |
| 618 | printf("\tx4=%-16lx\n" , regs.x4); |
| 619 | printf("\tx5=%-16lx\n" , regs.x5); |
| 620 | #else |
| 621 | #error unspport get_cpu_context |
| 622 | #endif |
| 623 | |
| 624 | if (out) { |
| 625 | memcpy(out, ®s, sizeof(regs)); |
| 626 | } |
| 627 | |
| 628 | return 0; |
| 629 | } |
| 630 | |
| 631 | #if defined(__i386__) || defined(__x86_64__) |
| 632 | #define BREAKPOINT_INSTRUCTION 0xcc |
| 633 | #define BREAKPOINT_SIZE 1 |
| 634 | #elif defined(__mips__) |
| 635 | #define BREAKPOINT_INSTRUCTION 0x0000000d |
| 636 | #define BREAKPOINT_SIZE 4 |
| 637 | #elif defined(__sw_64) |
| 638 | #define BREAKPOINT_INSTRUCTION 0x00000080 |
| 639 | #define BREAKPOINT_SIZE 4 |
| 640 | #elif defined(__aarch64__) |
| 641 | #define BREAKPOINT_INSTRUCTION 0xd4200000 |
| 642 | #define BREAKPOINT_SIZE 4 |
| 643 | #else |
| 644 | #error need implement in new arch |
| 645 | #endif |
| 646 | |
| 647 | int gdb_break(int tid, uintptr_t addr, uint32_t* value) |
| 648 | { |
| 649 | uint32_t ret; |
| 650 | uint32_t cc = BREAKPOINT_INSTRUCTION; |
| 651 | if (read_mem(tid, addr, &ret, BREAKPOINT_SIZE) != BREAKPOINT_SIZE) { |
| 652 | return -1; |
| 653 | } |
| 654 | if (write_mem(tid, addr, &cc, BREAKPOINT_SIZE) != BREAKPOINT_SIZE) { |
| 655 | return -2; |
| 656 | } |
| 657 | |
| 658 | if (value) *value = ret; |
| 659 | |
| 660 | return 0; |
| 661 | } |
| 662 | |
| 663 | int gdb_delete(int tid, uintptr_t addr, uint32_t old_value) |
| 664 | { |
| 665 | return write_mem(tid, addr, &old_value, BREAKPOINT_SIZE); |
| 666 | } |
| 667 | |
| 668 | // gdb_print(pid, base, "/usr/lib/x86_64-linux-gnu/ld-2.24.so", addr, "struct link_map"); |
| 669 | int gdb_print(int pid, uintptr_t module_base, const char* filename, |
| 670 | uintptr_t var_address, const char* type_name) |
| 671 | { |
| 672 | (void)pid; |
| 673 | (void)module_base; |
| 674 | (void)filename; |
| 675 | (void)var_address; |
| 676 | (void)type_name; |
| 677 | SymbolFile helper(module_base, filename, true); |
| 678 | /* |
| 679 | http://www.dwarfstd.org/doc/Debugging%20using%20DWARF-2012.pdf |
| 680 | |
| 681 | .debug_abbrev Abbreviations used in the .debug_info section |
| 682 | .debug_aranges A mapping between memory address and compilation |
| 683 | .debug_frame Call Frame Information |
| 684 | .debug_info The core DWARF data containing DWARF Information Entries (DIEs) |
| 685 | .debug_line Line Number Program |
| 686 | .debug_loc Location descriptions |
| 687 | .debug_macinfo Macro descriptions |
| 688 | .debug_pubnames A lookup table for global objects and functions |
| 689 | .debug_pubtypes A lookup table for global types |
| 690 | .debug_ranges Address ranges referenced by DIEs |
| 691 | .debug_str String table used by .debug_info |
| 692 | .debug_types Type descriptions |
| 693 | |
| 694 | for (const auto& cu : helper.m_dwarf.compilation_units()) { |
| 695 | for (const auto& die : cu.root()) { |
| 696 | if (die.has(dwarf::DW_AT::name) && at_name(die) == type_name) { |
| 697 | } |
| 698 | } |
| 699 | } |
| 700 | */ |
| 701 | |
| 702 | return 0; |
| 703 | } |
| 704 | |