[心得] 實做 c backtrace

看板C_and_CPP (C/C++)作者 (「雄辯是銀,沉默是金」)時間2周前 (2024/04/28 11:06), 2周前編輯推噓3(300)
留言3則, 3人參與, 1周前最新討論串1/1
c 的 backtrace 是在 call function 時, 如何得知誰 call 了這個 function。 c 的 backtrace 如何做到, 問 chatgpt 馬上就給出 list 1. 的範例程式, 真的好用, 以 list 2 來說: _start -> __libc_start_main -> main -> f1 -> f2 -> f3 -> print_backtrace 會得到 xx [0x400b7b] xx [0x400d60] xx [0x401087] xx [0x4010a4] xx [0x4010c0] xx [0x401689] xx [0x400a5a] 使用 addr2line 可以查到對應的 function addr2line -f -e t2 0x400b7b print_backtrace /home/descent/git/progs/backtrace/t2.c:15 addr2line -f -e t2 0x400a5a _start ??:? 會得到: print_backtrace -> f3 -> f2 -> f1 -> main -> __libc_start_main -> _start 但如果再問 chatgpt 要怎麼實做 backtrace(), backtrace_symbols(), 它就鬼打牆了。 所以如果不借助 backtrace(), backtrace_symbols() 要怎麼辦到呢? 這和平台有關, 本篇是在 x86_64 環境的實做。 list 1. print_backtrace() 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdint.h> 4 5 #include <execinfo.h> 6 7 #define BT_BUF_SIZE 100 8 9 void print_backtrace() 10 { 11 void *buffer[BT_BUF_SIZE]; 12 char **strings; 13 int nptrs; 14 15 nptrs = backtrace(buffer, BT_BUF_SIZE); 16 printf("backtrace() returned %d addresses\n", nptrs); 17 18 strings = backtrace_symbols(buffer, nptrs); 19 if (strings == NULL) { 20 perror("backtrace_symbols"); 21 exit(EXIT_FAILURE); 22 } 23 24 for (int i = 0; i < nptrs; i++) { 25 printf("xx %s\n", strings[i]); 26 } 27 28 free(strings); 29 } list 2. t2.c 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdint.h> 4 5 #include <execinfo.h> 6 7 #define BT_BUF_SIZE 100 8 9 void print_backtrace() 10 { 11 void *buffer[BT_BUF_SIZE]; 12 char **strings; 13 int nptrs; 14 15 nptrs = backtrace(buffer, BT_BUF_SIZE); 16 printf("backtrace() returned %d addresses\n", nptrs); 17 18 strings = backtrace_symbols(buffer, nptrs); 19 if (strings == NULL) { 20 perror("backtrace_symbols"); 21 exit(EXIT_FAILURE); 22 } 23 24 for (int i = 0; i < nptrs; i++) { 25 printf("xx %s\n", strings[i]); 26 } 27 28 free(strings); 29 } 30 31 void f5() 32 { 33 printf("in f5\n"); 34 print_backtrace(); 35 } 36 37 38 #include <stdio.h> 39 40 uintptr_t get_rip_value() 41 { 42 //uintptr_t rip_value; 43 //asm volatile("movq $0, %%rax; movq (%%rsp), %%rax" : "=a" (rip_value)); 44 //asm("mov %%rip, %0" : "=r" (rip_value)); 45 uintptr_t current_address; 46 asm("lea (%%rip), %0" : "=r" (current_address)); 47 return current_address; 48 } 49 50 unsigned long get_rbp() 51 { 52 unsigned long rbp_value; 53 asm("movq %%rbp, %0" : "=r" (rbp_value)); 54 printf("The value of RBP register is: %#lx\n", rbp_value); 55 return rbp_value; 56 } 57 58 int addr2func(uintptr_t addr) 59 { 60 char cmd[128] = {0}; 61 sprintf(cmd, "addr2line -f -e t2 %#lx\n", addr); 62 printf("cmd: %s\n", cmd); 63 system(cmd); 64 return 0; 65 } 66 67 void f3() 68 { 69 print_backtrace(); 70 #if 1 71 int level = 0; 72 printf("in f3\n"); 73 //while(1) 74 { 75 { 76 uintptr_t current_address; 77 asm("lea (%%rip), %0" : "=r" (current_address)); 78 printf("current_address : %#lx\n", current_address); 79 addr2func(current_address); 80 81 printf("======\n"); 82 83 unsigned long rbp_value, previous_rbp; 84 asm("movq %%rbp, %0" : "=r" (rbp_value)); 85 printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value); 86 ++level; 87 88 uintptr_t ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t)); 89 printf("ret_addr : %#lx\n", ret_addr); 90 91 addr2func(ret_addr); 92 93 printf("======\n"); 94 95 rbp_value = *(uintptr_t*)(rbp_value); 96 printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value); 97 ++level; 98 99 ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t)); 100 addr2func(ret_addr); 101 102 103 printf("======\n"); 104 105 rbp_value = *(uintptr_t*)(rbp_value); 106 printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value); 107 ++level; 108 109 ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t)); 110 addr2func(ret_addr); 111 112 printf("======\n"); 113 114 rbp_value = *(uintptr_t*)(rbp_value); 115 printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value); 116 ++level; 117 118 ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t)); 119 addr2func(ret_addr); 120 121 printf("======\n"); 122 123 previous_rbp = rbp_value; 124 125 rbp_value = *(uintptr_t*)(rbp_value); 126 printf("%d ## The value of RBP register is: %#lx, previous_rbp: %#lx\n", level, rbp_value, previous_rbp); 127 ++level; 128 129 if (rbp_value > previous_rbp) 130 { 131 ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t)); 132 addr2func(ret_addr); 133 } 134 else 135 { 136 printf("top stack frame\n"); 137 } 138 139 printf("======\n"); 140 141 142 void* frame_address = __builtin_frame_address(0); 143 if (frame_address) 144 printf("Frame 0 address of f3: %p\n", frame_address); 145 146 void* return_address = __builtin_return_address(0); 147 if (return_address) 148 printf("Return 0 address of f3: %p\n", return_address); 149 } 150 151 #if 1 152 { 153 void* frame_address = __builtin_frame_address(1); 154 if (frame_address) 155 printf("Frame 1 address of f3: %p\n", frame_address); 156 157 void* return_address = __builtin_return_address(1); 158 if (return_address) 159 printf("Return 1 address of f3: %p\n", return_address); 160 } 161 162 { 163 void* frame_address = __builtin_frame_address(2); 164 if (frame_address) 165 printf("Frame 2 address of f3: %p\n", frame_address); 166 167 void* return_address = __builtin_return_address(2); 168 if (return_address) 169 printf("Return 2 address of f3: %p\n", return_address); 170 } 171 #endif 172 #if 0 173 { 174 void* frame_address = __builtin_frame_address(3); 175 if (frame_address) 176 printf("Frame 3 address of f2: %p\n", frame_address); 177 178 void* return_address = __builtin_return_address(3); 179 if (return_address) 180 printf("Return 3 address of f2: %p\n", return_address); 181 } 182 #endif 183 184 } 185 #endif 186 187 #if 0 188 printf("in f2\n"); 189 f3(); 190 #endif 191 } 192 193 void f2() 194 { 195 printf("in f2\n"); 196 f3(); 197 } 198 199 void f1() 200 { 201 printf("in f1\n"); 202 f2(); 203 } 204 205 int main(int argc, char *argv[]) 206 { 207 f1(); 208 printf("main: %p\n", main); 209 printf("f1: %p\n", f1); 210 printf("f2: %p\n", f2); 211 #if 0 212 printf("f3: %p\n", f3); 213 printf("f5: %p\n", f5); 214 #endif 215 return 0; 216 } 先來理解 c 語言呼叫 function 時做的動作, 參考 list 3 的反組譯。 list 3. objdump -d t2 1 2 t2: file format elf64-x86-64 3 611 0000000000400c50 <f5>: 612 400c50: 55 push %rbp 613 400c51: 48 89 e5 mov %rsp,%rbp 614 400c54: 48 8d 3d 49 2c 09 00 lea 0x92c49(%rip),%rdi # 4938a4 <_IO_stdin_used+0x44> 615 400c5b: e8 f0 06 01 00 callq 411350 <_IO_puts> 616 400c60: b8 00 00 00 00 mov $0x0,%eax 617 400c65: e8 e3 fe ff ff callq 400b4d <print_backtrace> 618 400c6a: 90 nop 619 400c6b: 5d pop %rbp 620 400c6c: c3 retq 621 682 683 0000000000400d4e <f3>: 684 400d4e: 55 push %rbp 685 400d4f: 48 89 e5 mov %rsp,%rbp 686 400d52: 48 83 ec 60 sub $0x60,%rsp 687 400d56: b8 00 00 00 00 mov $0x0,%eax 688 400d5b: e8 ed fd ff ff callq 400b4d <print_backtrace> 689 400d60: c7 45 ac 00 00 00 00 movl $0x0,-0x54(%rbp) 690 400d67: 48 8d 3d 88 2b 09 00 lea 0x92b88(%rip),%rdi # 4938f6 <_IO_stdin_used+0x96> 691 400d6e: e8 dd 05 01 00 callq 411350 <_IO_puts> 692 400d73: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 400d7a <f3+0x2c> 693 400d7a: 48 89 45 b0 mov %rax,-0x50(%rbp) 694 400d7e: 48 8b 45 b0 mov -0x50(%rbp),%rax 695 400d82: 48 89 c6 mov %rax,%rsi 696 400d85: 48 8d 3d 70 2b 09 00 lea 0x92b70(%rip),%rdi # 4938fc <_IO_stdin_used+0x9c> 697 400d8c: b8 00 00 00 00 mov $0x0,%eax 698 400d91: e8 fa f3 00 00 callq 410190 <_IO_printf> 699 400d96: 48 8b 45 b0 mov -0x50(%rbp),%rax 700 400d9a: 48 89 c7 mov %rax,%rdi 701 400d9d: e8 0d ff ff ff callq 400caf <addr2func> 702 400da2: 48 8d 3d 6b 2b 09 00 lea 0x92b6b(%rip),%rdi # 493914 <_IO_stdin_used+0xb4> 703 400da9: e8 a2 05 01 00 callq 411350 <_IO_puts> 704 400dae: 48 89 e8 mov %rbp,%rax 705 400db1: 48 89 45 b8 mov %rax,-0x48(%rbp) 706 400db5: 48 8b 55 b8 mov -0x48(%rbp),%rdx 707 400db9: 8b 45 ac mov -0x54(%rbp),%eax 708 400dbc: 89 c6 mov %eax,%esi 709 400dbe: 48 8d 3d 5b 2b 09 00 lea 0x92b5b(%rip),%rdi # 493920 <_IO_stdin_used+0xc0> 710 400dc5: b8 00 00 00 00 mov $0x0,%eax 711 400dca: e8 c1 f3 00 00 callq 410190 <_IO_printf> 712 400dcf: 83 45 ac 01 addl $0x1,-0x54(%rbp) 713 400dd3: 48 8b 45 b8 mov -0x48(%rbp),%rax 714 400dd7: 48 83 c0 08 add $0x8,%rax 715 400ddb: 48 8b 00 mov (%rax),%rax 716 400dde: 48 89 45 c0 mov %rax,-0x40(%rbp) 717 400de2: 48 8b 45 c0 mov -0x40(%rbp),%rax 718 400de6: 48 89 c6 mov %rax,%rsi 719 400de9: 48 8d 3d 5a 2b 09 00 lea 0x92b5a(%rip),%rdi # 49394a <_IO_stdin_used+0xea> 720 400df0: b8 00 00 00 00 mov $0x0,%eax 721 400df5: e8 96 f3 00 00 callq 410190 <_IO_printf> 722 400dfa: 48 8b 45 c0 mov -0x40(%rbp),%rax 723 400dfe: 48 89 c7 mov %rax,%rdi 724 400e01: e8 a9 fe ff ff callq 400caf <addr2func> 725 400e06: 48 8d 3d 07 2b 09 00 lea 0x92b07(%rip),%rdi # 493914 <_IO_stdin_used+0xb4> 726 400e0d: e8 3e 05 01 00 callq 411350 <_IO_puts> 727 400e12: 48 8b 45 b8 mov -0x48(%rbp),%rax 728 400e16: 48 8b 00 mov (%rax),%rax 729 400e19: 48 89 45 b8 mov %rax,-0x48(%rbp) 730 400e1d: 48 8b 55 b8 mov -0x48(%rbp),%rdx 731 400e21: 8b 45 ac mov -0x54(%rbp),%eax 732 400e24: 89 c6 mov %eax,%esi 733 400e26: 48 8d 3d f3 2a 09 00 lea 0x92af3(%rip),%rdi # 493920 <_IO_stdin_used+0xc0> 734 400e2d: b8 00 00 00 00 mov $0x0,%eax 735 400e32: e8 59 f3 00 00 callq 410190 <_IO_printf> 736 400e37: 83 45 ac 01 addl $0x1,-0x54(%rbp) 737 400e3b: 48 8b 45 b8 mov -0x48(%rbp),%rax 738 400e3f: 48 83 c0 08 add $0x8,%rax 739 400e43: 48 8b 00 mov (%rax),%rax 740 400e46: 48 89 45 c0 mov %rax,-0x40(%rbp) 741 400e4a: 48 8b 45 c0 mov -0x40(%rbp),%rax 742 400e4e: 48 89 c7 mov %rax,%rdi 743 400e51: e8 59 fe ff ff callq 400caf <addr2func> 744 400e56: 48 8d 3d b7 2a 09 00 lea 0x92ab7(%rip),%rdi # 493914 <_IO_stdin_used+0xb4> 745 400e5d: e8 ee 04 01 00 callq 411350 <_IO_puts> 746 400e62: 48 8b 45 b8 mov -0x48(%rbp),%rax 747 400e66: 48 8b 00 mov (%rax),%rax 748 400e69: 48 89 45 b8 mov %rax,-0x48(%rbp) 749 400e6d: 48 8b 55 b8 mov -0x48(%rbp),%rdx 750 400e71: 8b 45 ac mov -0x54(%rbp),%eax 751 400e74: 89 c6 mov %eax,%esi 752 400e76: 48 8d 3d a3 2a 09 00 lea 0x92aa3(%rip),%rdi # 493920 <_IO_stdin_used+0xc0> 753 400e7d: b8 00 00 00 00 mov $0x0,%eax 754 400e82: e8 09 f3 00 00 callq 410190 <_IO_printf> 755 400e87: 83 45 ac 01 addl $0x1,-0x54(%rbp) 756 400e8b: 48 8b 45 b8 mov -0x48(%rbp),%rax 757 400e8f: 48 83 c0 08 add $0x8,%rax 758 400e93: 48 8b 00 mov (%rax),%rax 759 400e96: 48 89 45 c0 mov %rax,-0x40(%rbp) 760 400e9a: 48 8b 45 c0 mov -0x40(%rbp),%rax 761 400e9e: 48 89 c7 mov %rax,%rdi 762 400ea1: e8 09 fe ff ff callq 400caf <addr2func> 763 400ea6: 48 8d 3d 67 2a 09 00 lea 0x92a67(%rip),%rdi # 493914 <_IO_stdin_used+0xb4> 764 400ead: e8 9e 04 01 00 callq 411350 <_IO_puts> 765 400eb2: 48 8b 45 b8 mov -0x48(%rbp),%rax 766 400eb6: 48 8b 00 mov (%rax),%rax 767 400eb9: 48 89 45 b8 mov %rax,-0x48(%rbp) 768 400ebd: 48 8b 55 b8 mov -0x48(%rbp),%rdx 769 400ec1: 8b 45 ac mov -0x54(%rbp),%eax 770 400ec4: 89 c6 mov %eax,%esi 771 400ec6: 48 8d 3d 53 2a 09 00 lea 0x92a53(%rip),%rdi # 493920 <_IO_stdin_used+0xc0> 772 400ecd: b8 00 00 00 00 mov $0x0,%eax 773 400ed2: e8 b9 f2 00 00 callq 410190 <_IO_printf> 774 400ed7: 83 45 ac 01 addl $0x1,-0x54(%rbp) 775 400edb: 48 8b 45 b8 mov -0x48(%rbp),%rax 776 400edf: 48 83 c0 08 add $0x8,%rax 777 400ee3: 48 8b 00 mov (%rax),%rax 778 400ee6: 48 89 45 c0 mov %rax,-0x40(%rbp) 779 400eea: 48 8b 45 c0 mov -0x40(%rbp),%rax 780 400eee: 48 89 c7 mov %rax,%rdi 781 400ef1: e8 b9 fd ff ff callq 400caf <addr2func> 782 400ef6: 48 8d 3d 17 2a 09 00 lea 0x92a17(%rip),%rdi # 493914 <_IO_stdin_used+0xb4> 783 400efd: e8 4e 04 01 00 callq 411350 <_IO_puts> 784 400f02: 48 8b 45 b8 mov -0x48(%rbp),%rax 785 400f06: 48 89 45 c8 mov %rax,-0x38(%rbp) 786 400f0a: 48 8b 45 b8 mov -0x48(%rbp),%rax 787 400f0e: 48 8b 00 mov (%rax),%rax 788 400f11: 48 89 45 b8 mov %rax,-0x48(%rbp) 789 400f15: 48 8b 4d c8 mov -0x38(%rbp),%rcx 790 400f19: 48 8b 55 b8 mov -0x48(%rbp),%rdx 791 400f1d: 8b 45 ac mov -0x54(%rbp),%eax 792 400f20: 89 c6 mov %eax,%esi 793 400f22: 48 8d 3d 37 2a 09 00 lea 0x92a37(%rip),%rdi # 493960 <_IO_stdin_used+0x100> 794 400f29: b8 00 00 00 00 mov $0x0,%eax 795 400f2e: e8 5d f2 00 00 callq 410190 <_IO_printf> 796 400f33: 83 45 ac 01 addl $0x1,-0x54(%rbp) 797 400f37: 48 8b 45 b8 mov -0x48(%rbp),%rax 798 400f3b: 48 3b 45 c8 cmp -0x38(%rbp),%rax 799 400f3f: 76 1d jbe 400f5e <f3+0x210> 800 400f41: 48 8b 45 b8 mov -0x48(%rbp),%rax 801 400f45: 48 83 c0 08 add $0x8,%rax 802 400f49: 48 8b 00 mov (%rax),%rax 803 400f4c: 48 89 45 c0 mov %rax,-0x40(%rbp) 804 400f50: 48 8b 45 c0 mov -0x40(%rbp),%rax 805 400f54: 48 89 c7 mov %rax,%rdi 806 400f57: e8 53 fd ff ff callq 400caf <addr2func> 807 400f5c: eb 0c jmp 400f6a <f3+0x21c> 808 400f5e: 48 8d 3d 39 2a 09 00 lea 0x92a39(%rip),%rdi # 49399e <_IO_stdin_used+0x13e> 809 400f65: e8 e6 03 01 00 callq 411350 <_IO_puts> 810 400f6a: 48 8d 3d a3 29 09 00 lea 0x929a3(%rip),%rdi # 493914 <_IO_stdin_used+0xb4> 811 400f71: e8 da 03 01 00 callq 411350 <_IO_puts> 812 400f76: 48 89 6d d0 mov %rbp,-0x30(%rbp) 813 400f7a: 48 83 7d d0 00 cmpq $0x0,-0x30(%rbp) 814 400f7f: 74 18 je 400f99 <f3+0x24b> 815 400f81: 48 8b 45 d0 mov -0x30(%rbp),%rax 816 400f85: 48 89 c6 mov %rax,%rsi 817 400f88: 48 8d 3d 1f 2a 09 00 lea 0x92a1f(%rip),%rdi # 4939ae <_IO_stdin_used+0x14e> 818 400f8f: b8 00 00 00 00 mov $0x0,%eax 819 400f94: e8 f7 f1 00 00 callq 410190 <_IO_printf> 820 400f99: 48 8b 45 08 mov 0x8(%rbp),%rax 821 400f9d: 48 89 45 d8 mov %rax,-0x28(%rbp) 822 400fa1: 48 83 7d d8 00 cmpq $0x0,-0x28(%rbp) 823 400fa6: 74 18 je 400fc0 <f3+0x272> 824 400fa8: 48 8b 45 d8 mov -0x28(%rbp),%rax 825 400fac: 48 89 c6 mov %rax,%rsi 826 400faf: 48 8d 3d 13 2a 09 00 lea 0x92a13(%rip),%rdi # 4939c9 <_IO_stdin_used+0x169> 827 400fb6: b8 00 00 00 00 mov $0x0,%eax 828 400fbb: e8 d0 f1 00 00 callq 410190 <_IO_printf> 829 400fc0: 48 8b 45 00 mov 0x0(%rbp),%rax 830 400fc4: 48 89 45 e0 mov %rax,-0x20(%rbp) 831 400fc8: 48 83 7d e0 00 cmpq $0x0,-0x20(%rbp) 832 400fcd: 74 18 je 400fe7 <f3+0x299> 833 400fcf: 48 8b 45 e0 mov -0x20(%rbp),%rax 834 400fd3: 48 89 c6 mov %rax,%rsi 835 400fd6: 48 8d 3d 08 2a 09 00 lea 0x92a08(%rip),%rdi # 4939e5 <_IO_stdin_used+0x185> 836 400fdd: b8 00 00 00 00 mov $0x0,%eax 837 400fe2: e8 a9 f1 00 00 callq 410190 <_IO_printf> 838 400fe7: 48 8b 45 00 mov 0x0(%rbp),%rax 839 400feb: 48 8b 40 08 mov 0x8(%rax),%rax 840 400fef: 48 89 45 e8 mov %rax,-0x18(%rbp) 841 400ff3: 48 83 7d e8 00 cmpq $0x0,-0x18(%rbp) 842 400ff8: 74 18 je 401012 <f3+0x2c4> 843 400ffa: 48 8b 45 e8 mov -0x18(%rbp),%rax 844 400ffe: 48 89 c6 mov %rax,%rsi 845 401001: 48 8d 3d f8 29 09 00 lea 0x929f8(%rip),%rdi # 493a00 <_IO_stdin_used+0x1a0> 846 401008: b8 00 00 00 00 mov $0x0,%eax 847 40100d: e8 7e f1 00 00 callq 410190 <_IO_printf> 848 401012: 48 8b 45 00 mov 0x0(%rbp),%rax 849 401016: 48 8b 00 mov (%rax),%rax 850 401019: 48 89 45 f0 mov %rax,-0x10(%rbp) 851 40101d: 48 83 7d f0 00 cmpq $0x0,-0x10(%rbp) 852 401022: 74 18 je 40103c <f3+0x2ee> 853 401024: 48 8b 45 f0 mov -0x10(%rbp),%rax 854 401028: 48 89 c6 mov %rax,%rsi 855 40102b: 48 8d 3d ea 29 09 00 lea 0x929ea(%rip),%rdi # 493a1c <_IO_stdin_used+0x1bc> 856 401032: b8 00 00 00 00 mov $0x0,%eax 857 401037: e8 54 f1 00 00 callq 410190 <_IO_printf> 858 40103c: 48 8b 45 00 mov 0x0(%rbp),%rax 859 401040: 48 8b 00 mov (%rax),%rax 860 401043: 48 8b 40 08 mov 0x8(%rax),%rax 861 401047: 48 89 45 f8 mov %rax,-0x8(%rbp) 862 40104b: 48 83 7d f8 00 cmpq $0x0,-0x8(%rbp) 863 401050: 74 18 je 40106a <f3+0x31c> 864 401052: 48 8b 45 f8 mov -0x8(%rbp),%rax 865 401056: 48 89 c6 mov %rax,%rsi 866 401059: 48 8d 3d d7 29 09 00 lea 0x929d7(%rip),%rdi # 493a37 <_IO_stdin_used+0x1d7> 867 401060: b8 00 00 00 00 mov $0x0,%eax 868 401065: e8 26 f1 00 00 callq 410190 <_IO_printf> 869 40106a: 90 nop 870 40106b: c9 leaveq 871 40106c: c3 retq 872 873 000000000040106d <f2>: 874 40106d: 55 push %rbp 875 40106e: 48 89 e5 mov %rsp,%rbp 876 401071: 48 8d 3d db 29 09 00 lea 0x929db(%rip),%rdi # 493a53 <_IO_stdin_used+0x1f3> 877 401078: e8 d3 02 01 00 callq 411350 <_IO_puts> 878 40107d: b8 00 00 00 00 mov $0x0,%eax 879 401082: e8 c7 fc ff ff callq 400d4e <f3> 880 401087: 90 nop 881 401088: 5d pop %rbp 882 401089: c3 retq 883 884 000000000040108a <f1>: 885 40108a: 55 push %rbp 886 40108b: 48 89 e5 mov %rsp,%rbp 887 40108e: 48 8d 3d c4 29 09 00 lea 0x929c4(%rip),%rdi # 493a59 <_IO_stdin_used+0x1f9> 888 401095: e8 b6 02 01 00 callq 411350 <_IO_puts> 889 40109a: b8 00 00 00 00 mov $0x0,%eax 890 40109f: e8 c9 ff ff ff callq 40106d <f2> 891 4010a4: 90 nop 892 4010a5: 5d pop %rbp 893 4010a6: c3 retq 894 895 00000000004010a7 <main>: 896 4010a7: 55 push %rbp 897 4010a8: 48 89 e5 mov %rsp,%rbp 898 4010ab: 48 83 ec 10 sub $0x10,%rsp 899 4010af: 89 7d fc mov %edi,-0x4(%rbp) 900 4010b2: 48 89 75 f0 mov %rsi,-0x10(%rbp) 901 4010b6: b8 00 00 00 00 mov $0x0,%eax 902 4010bb: e8 ca ff ff ff callq 40108a <f1> 903 4010c0: 48 8d 35 e0 ff ff ff lea -0x20(%rip),%rsi # 4010a7 <main> 904 4010c7: 48 8d 3d 91 29 09 00 lea 0x92991(%rip),%rdi # 493a5f <_IO_stdin_used+0x1ff> 905 4010ce: b8 00 00 00 00 mov $0x0,%eax 906 4010d3: e8 b8 f0 00 00 callq 410190 <_IO_printf> 907 4010d8: 48 8d 35 ab ff ff ff lea -0x55(%rip),%rsi # 40108a <f1> 908 4010df: 48 8d 3d 83 29 09 00 lea 0x92983(%rip),%rdi # 493a69 <_IO_stdin_used+0x209> 909 4010e6: b8 00 00 00 00 mov $0x0,%eax 910 4010eb: e8 a0 f0 00 00 callq 410190 <_IO_printf> 911 4010f0: 48 8d 35 76 ff ff ff lea -0x8a(%rip),%rsi # 40106d <f2> 912 4010f7: 48 8d 3d 73 29 09 00 lea 0x92973(%rip),%rdi # 493a71 <_IO_stdin_used+0x211> 913 4010fe: b8 00 00 00 00 mov $0x0,%eax 914 401103: e8 88 f0 00 00 callq 410190 <_IO_printf> 915 401108: b8 00 00 00 00 mov $0x0,%eax 916 40110d: c9 leaveq 917 40110e: c3 retq L902 main call f1, L884 ~ L886 是進入 f1 時做的事情。 902 4010bb: e8 ca ff ff ff callq 40108a <f1> main call f1 時, f1 會做 884 000000000040108a <f1>: 885 40108a: 55 push %rbp 886 40108b: 48 89 e5 mov %rsp,%rbp 890 40109f: e8 c9 ff ff ff callq 40106d <f2> f1 call f2 時, f2 會做 873 000000000040106d <f2>: 874 40106d: 55 push %rbp 875 40106e: 48 89 e5 mov %rsp,%rbp push %rbp mov %rsp,%rbp 都是在 function 的最開始時會做的事情 (table 1. L2, L4)。table 1. main call f1, f1 call f2 把 rsp 的內容整理起來。 table 1. main call f1; f1 call f2 stack 內容 https://photos.app.goo.gl/FCQy7GtyYKuFnHXd6 指令 call 會發生的 sp 操作: rsp - 8, 再把 ret address 放入 rsp, 我用 gdb 把從 main 到 f2 時的 stack 內容記錄在 table 1。 list 5. t2 的編譯指令 gcc -static -Wall -save-temps -g -no-pie t2.c -o t2 用了 -no-pie 是希望不要編譯成 relocation 的執行檔, 用 objdump 在對照位址時比較 方便。其他編譯選項沒太大影響。 原理是這樣, 先抓到目前 rbp 的值, 假如目前在 f2, 抓到 f2_rpb 就可以知道 f1 function 的 rpb f1_rpb, 知道了 f1_rpb 就可以知道 main function 的 rpb, 那麼知道每個 function 的 rbp 要幹麻呢? 為了取得 return address, 怎麼取得, 每層 function的 rbp + 8 的位址就可以得到 (對照 table 1. L1, L3)。 寫成 c code 就是: 上一層 function 的 return address = *(uintptr_t*)(rpb + 8) 那麼又怎麼從目前 function 的 rpb 值得到上一層的 rpb 值呢? 從 rpb 值的位址取得 (參考 table 1. L2, L4), 寫成 c code 就是: 上一層 function 的 rpb = *(uintptr_t*)(rpb) 這樣一層一層追, 就可以追到 __libc_start_main, 那 _start 追得到嗎? 抱歉, 目前我還不知道怎麼從 __libc_start_main 追到 _start (backtrace(), backtrace_symbols() 可以追到 _start)。 另外還有一個問題, 從這層的 rpb 一直追到上一層的 rpb, 要怎麼判 定追到 __libc_start_main 這層了, 這邊是觀察出來的, 上一層的 rpb 應該會比目前這層的 rpb 大, 我就這樣判定, 有可能會出錯嗎? 當然有可能, 但我想不到別的辦法了。 list 6 L105, L106 有類似的檢查條件。 list 2. L81 ~ L139 就是在做這樣的事情。另外還需要知道目前在那個 function, 所以 用了 L77 得到目前的位址。這些抓暫存器、抓目前位址都是透過 chatgpt 問到, 相當方 便。 再來有了位址要怎麼找出對應的 function, 這邊我偷懶了, 直接使用 addr2line 這個指 令幫忙。光是 addr2line 怎麼辦到的, 可能又是一個主題了。 list 2 L142, L146 就是在取得 rbp 和 return address, __builtin_frame_address(), __builtin_return_address() 是 gcc 內建 function, 比較有可攜性。 我不滿足這樣的作法, 想「知道」怎麼辦到的, 才有了本篇文章。 在 glibc 2.39 sysdeps/i386/backtrace.c __backtrace (void **array, int size) 可以看到類似的作法。 ref: 誰在呼叫我?不同的backtrace實作說明好文章 https://reborn2266.blogspot.com/2011/12/backtrace.html blog 版本: https://descent-incoming.blogspot.com/2024/03/c-backtrace.html?m=0 -- 我不是無所不知,只是剛好知道而已。 -- ※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 116.89.130.166 (臺灣) ※ 文章網址: https://www.ptt.cc/bbs/C_and_CPP/M.1714273592.A.2B3.html

04/28 12:12, 2周前 , 1F
只是要用的話好像有個libunwind
04/28 12:12, 1F
這個一樣問 chatgpt 就會給一個範例。

04/29 13:25, 2周前 , 2F
酷~
04/29 13:25, 2F
※ 編輯: descent (180.217.159.220 臺灣), 04/29/2024 17:08:17

05/01 07:10, 1周前 , 3F
05/01 07:10, 3F
文章代碼(AID): #1cBRquAp (C_and_CPP)
文章代碼(AID): #1cBRquAp (C_and_CPP)