2024年4月26日 星期五

兌換台灣大哥大電子發票

台灣大哥大寄了實體信件通知電子發票中獎, 202312 帳單中獎, 會需要 202312 帳單上的資料, 到 7-11 印出中獎發票。

麻煩的是我沒 202312 帳單, 都是從台灣大哥大 app 繳款, 所以要印出中獎發票有點麻煩。

從 mail 的 qr code 連到 https://ow.tstarcs.taiwanmobile.com/TWM/electronicInvoice.php, 裡頭有發票資訊, 找出中獎的那期, 以我的例子是 202401 那期。記住載具號碼, 那個可以在 7-11 ibon 印出電子發票; 另外還需要載具類別, 台灣大哥大固定是 EJ0118, 這些資訊當期帳單都有, 所以沒有帳單會比較麻煩,
拿到 7-11 兌換會有 2 元印花稅。

2024年4月21日 星期日

借閱電子版漫畫 - hyread/台灣雲端書庫

hyread app 可以借閱圖書館書籍, 來試試看。加入某個圖書館, 需要有帳號, 之前有申請借書證就應該會有。



最近開始借閱圖書館電子漫畫, 覺得好像不用自己買電子版, 閱讀體驗和自己買的電子版完全一樣, 不知道圖書館的電子版是不是永久存在, 還是也會下架。反正購買的電子書也不能擁有, 在圖書館借閱也不會擁有電子書, 實在沒動機購買電子版本。



我會借閱漫畫和雜誌的部份, 漫畫和雜誌還勉強可以用 LCD 螢幕看; 書籍還是用 e-ink 螢幕看比較適合, 我沒辦法在 LCD 螢幕看大篇文字。

台灣雲端書庫 app 也可以借閱電子書, 一樣是用圖書館帳號登入。魔法使的 5, 6 hyread 不知道為什麼沒有, 台灣雲端書庫則是可以借閱。


ref:
電子書借閱新政上路 每人無上限且回饋補助作者

2024年4月12日 星期五

signal handle backtrace (0) - signal handle 如何 return

我不是無所不知, 只是剛好知道而已。
實做 c backtrace」介紹了怎麼實作 backtrace, 下一個議題是: 如果從 signal handle 執行 backtrace 的動作, 能正常追到源頭呼叫的人嗎?

應該可以吧, 你這麼認為, 覺得在 signal handle 執行 backtrace 和在一般 function 執行 backtrace 不應該有什麼不同才是。

在這之前可以先想想看, 你寫的程式要怎麼辦到在 signal 來時, 切斷正在執行的部份, 然後轉到 signal handle 執行, 最後還可以接回原本的地方執行。

先來看看當 signal handle 發動的時候, 整個處理流程是怎麼樣? 設計了一個小程式來做這樣的實驗, 按照 t1.c 的程式, 有 2 個 thread, 一個停在 L255 while, 一個停在 L384 while, 這時候 kill pid 會得到怎麼樣的 backtrace 呢?

list 1
main thread pid: 27873
main thread tid: 27873
123 thread pid: 27873
123 thread tid: 27874

list 1 列出 2 個 thread 的 pid, tid。一個是 main thread, 一個是用 pthread_create() 建立的 thread。gettid 用法在 t1.c L16 ~ L20。

list 2 by glibc backtrace()
如果 kill 27873 會得到 main thread 的 backtrace
print_backtrace -> sig_handler -> __restore_rt -> fun_223 -> fun_22 -> main -> __libc_start_main -> _start

如果 kill 27874 會得到 thread_1 thread 的 backtrace
print_backtrace -> sig_handler -> __restore_rt -> fun_123 -> fun_12 -> fun_1 -> start_thread -> __clone

list 2. 使用 glibc backtrace() 得到的結果。

如果是由我自己藉由 rpb 追朔 stack 就不是這樣, 少了一部分的資訊。list3 少了 list 2 紅色部份, fun_223, fun_123

list 3 by stack rbp
如果 kill 27873 會得到 main thread 的 backtrace
print_backtrace -> sig_handler -> __restore_rt -> fun_22 -> main -> __libc_start_main 

如果 kill 27874 會得到 thread_1 thread 的 backtrace
print_backtrace -> sig_handler -> __restore_rt -> fun_12 -> fun_1 -> start_thread 

對於這樣的結果覺得很不爽, 為什麼從 stack frame 無法追朔完整, 而 backtrace() 可以。原因很複雜, 晚點再說。

先來看看為什麼有一個沒出現的 function - __restore_rt。

t1.c
  1 #include <signal.h>
  2 #include <pthread.h>
  3 #include <errno.h>
  4 #include <stdlib.h>
  5 #include <stdio.h>
  6 #include <string.h>
  7 #include <sys/types.h>
  8 #include <stdint.h>
  9 
 10 //#define _GNU_SOURCE
 11 #include <unistd.h>
 12 
 13 #include <unistd.h>
 14 #include <sys/syscall.h>
 15 
 16 #ifndef SYS_gettid
 17 #error "SYS_gettid unavailable on this system"
 18 #endif
 19 
 20 #define gettid() ((pid_t)syscall(SYS_gettid))
 21 
 22 /* Simple error handling functions */
 23 
 24 #define handle_error_en(en, msg) \
 25                do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
 26 
 27 #include <execinfo.h>
 28 
 29 #if 1
 30 #define BT_BUF_SIZE 100
 31 
 32 int addr2func(uintptr_t addr)
 33 {
 34   char cmd[128] = {0};
 35   sprintf(cmd, "addr2line -f -e ./t1 %#lx\n", addr);
 36   printf("cmd: %s\n", cmd);
 37   system(cmd);
 38   return 0;
 39 }
 40 
 41 #if 1
 42 uintptr_t get_rip_value() 
 43 {
 44   //uintptr_t rip_value;
 45   //asm volatile("movq $0, %%rax; movq (%%rsp), %%rax" : "=a" (rip_value));
 46   //asm("mov %%rip, %0" : "=r" (rip_value));
 47   uintptr_t current_address;
 48   asm("lea (%%rip), %0" : "=r" (current_address));
 49   return current_address;
 50 }
 51 
 52 unsigned long get_rbp()
 53 {
 54   unsigned long rbp_value;
 55   asm("movq %%rbp, %0" : "=r" (rbp_value));
 56   printf("The value of RBP register is: %#lx\n", rbp_value);
 57   return rbp_value;
 58 }
 59 
 60 #endif
 61 
 62 void print_backtrace() 
 63 {
 64     void *buffer[BT_BUF_SIZE];
 65     char **strings;
 66     int nptrs;
 67 
 68     nptrs = backtrace(buffer, BT_BUF_SIZE);
 69     printf("backtrace() returned %d addresses\n", nptrs);
 70 
 71     strings = backtrace_symbols(buffer, nptrs);
 72     if (strings == NULL) {
 73         perror("backtrace_symbols");
 74         exit(EXIT_FAILURE);
 75     }
 76 
 77     for (int i = 0; i < nptrs; i++) 
 78     {
 79       char cmd[128]={0};
 80       printf("nn %s\n", strings[i]);
 81       addr2func( (uintptr_t)buffer[i]);
 82       #if 0
 83       //printf("aa %#x\n", buffer[i]);
 84       sprintf(cmd, "/usr/bin/addr2line -f -e ./t123 %p\n", buffer[i]);
 85       //printf("cmd: %s\n", cmd);
 86       FILE *fs = popen(cmd, "r");
 87       if (fs)
 88       {
 89         char data[128];
 90         fread(data, 1, 128, fs);
 91         printf("data: %s\n", data);
 92       }
 93       pclose(fs);
 94       #endif
 95       
 96 
 97     }
 98 
 99     free(strings);
100 }
101 #else
102 
103 
104 #if 0
105 #define UNW_LOCAL_ONLY
106 #include <libunwind.h>
107 #endif
108 
109 
110 
111 void print_backtrace() {
112     unw_cursor_t cursor;
113     unw_context_t context;
114 
115     // 获取当前线程的上下文
116     unw_getcontext(&context);
117     // 初始化游标以便从当前位置开始遍历堆栈帧
118     unw_init_local(&cursor, &context);
119 
120     // 遍历堆栈帧
121     while (unw_step(&cursor) > 0) {
122         unw_word_t offset, pc;
123         char sym[256];
124 
125         // 获取指令指针位置
126         unw_get_reg(&cursor, UNW_REG_IP, &pc);
127         // 获取调用指令的偏移量
128         unw_get_proc_name(&cursor, sym, sizeof(sym), &offset);
129 
130         printf("  0x%lx: (%s+0x%lx)\n", (long)pc, sym, offset);
131     }
132 }
133 
134 #endif
135 
136 void sig_handler(int signo) 
137 {
138   int level = 0;
139   printf("in sig_handler\n");
140   #if 1
141 
142   //printf ("Caller name: %p\n", __builtin_return_address(0));
143   print_backtrace();
144 
145   #if 1
146   printf("in sig_handler pid: %d\n", getpid());
147   printf("in sig_handler tid: %d\n", gettid());
148   if (signo == SIGINT)
149     printf("Received SIGINT\n");
150   #endif
151   #endif
152   #if 1
153     {
154       uintptr_t current_address;
155       asm("lea (%%rip), %0" : "=r" (current_address));
156       printf("current_address : %#lx\n", current_address);
157       addr2func(current_address);
158 
159       printf("======\n");
160 
161       unsigned long rbp_value, previous_rbp;
162       asm("movq %%rbp, %0" : "=r" (rbp_value));
163       printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value);
164       ++level;
165 
166       uintptr_t ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t));
167       printf("ret_addr : %#lx\n", ret_addr);
168 
169       addr2func(ret_addr);
170 
171       printf("======\n");
172 
173       rbp_value = *(uintptr_t*)(rbp_value);
174       printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value);
175       ++level;
176 
177       ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t));
178       addr2func(ret_addr);
179 
180 
181       printf("======\n");
182 
183       rbp_value = *(uintptr_t*)(rbp_value);
184       printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value);
185       ++level;
186 
187       ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t));
188       addr2func(ret_addr);
189 
190       printf("======\n");
191 
192       rbp_value = *(uintptr_t*)(rbp_value);
193       printf("%d ## The value of RBP register is: %#lx\n", level, rbp_value);
194       ++level;
195 
196       ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t));
197       addr2func(ret_addr);
198 
199       printf("======\n");
200 
201       previous_rbp = rbp_value;
202       
203       rbp_value = *(uintptr_t*)(rbp_value);
204       printf("%d ## The value of RBP register is: %#lx, previous_rbp: %#lx\n", level, rbp_value, previous_rbp);
205       ++level;
206 
207       if (rbp_value > previous_rbp)
208       {
209         ret_addr = *(uintptr_t*)(rbp_value + sizeof(uintptr_t));
210         addr2func(ret_addr);
211       }
212       else
213       {
214         printf("top stack frame\n");
215       }
216 
217       printf("======\n");
218 
219 
220     void* frame_address = __builtin_frame_address(0);
221     if (frame_address)
222       printf("Frame 0 address of f3: %p\n", frame_address);
223 
224     void* return_address = __builtin_return_address(0);
225     if (return_address)
226       printf("Return 0 address of f3: %p\n", return_address);
227     }
228     #endif
229 }
230 
231 static void * sig_thread (void *arg)
232 {
233   sigset_t *set = arg;
234   int s, sig;
235 
236   printf("sig thread pid: %d\n", getpid());
237 
238   for (;;)
239     {
240       s = sigwait (set, &sig);
241       if (s != 0)
242  handle_error_en (s, "sigwait");
243       printf ("Signal handling thread got signal %d\n", sig);
244     }
245     return 0;
246 }
247 
248 static void fun_123(void *arg)
249 {
250   sigset_t *set = arg;
251   int s, sig;
252   printf("123 thread pid: %d\n", getpid());
253   printf("123 thread tid: %d\n", gettid());
254 
255   while(1)
256   {
257     //printf("123\n");
258     
259  
260 #if 0 
261    for (;;)
262      {
263        s = sigwait (set, &sig);
264        if (s != 0)
265          handle_error_en (s, "sigwait");
266        printf ("Signal handling thread got signal %d\n", sig);
267        print_backtrace();
268      }
269 #endif
270   }
271 }
272 
273 static void fun_12(void *arg)
274 {
275   fun_123(arg);
276 }
277 
278 static void * fun_1(void *arg)
279 {
280   //for (;;)
281   {
282     fun_12(arg);
283   #if 0
284     printf("11 sig thread pid: %d\n", getpid());
285     printf("11 sig thread tid: %d\n", gettid());
286   #endif
287   }
288   return 0;
289 }
290 
291 static void fun_223()
292 {
293   while(1)
294   {
295     //printf("223\n");
296   }
297 }
298 
299 static void fun_22()
300 {
301   fun_223();
302 }
303 
304 static void * fun_2(void *arg)
305 {
306   struct sched_param schedp;
307 
308   memset(&schedp, 0, sizeof(schedp));
309   schedp.sched_priority = 39;
310   sched_setscheduler(0, SCHED_RR, &schedp);
311 
312   for (;;)
313   {
314     fun_22();
315   #if 0
316     printf("22 sig thread pid: %d\n", getpid());
317     printf("22 sig thread tid: %d\n", gettid());
318   #endif
319   }
320   return 0;
321 }
322 
323 int main(int argc, char *argv[])
324 {
325   //pthread_t thread;
326   pthread_t thread_1;
327   //pthread_t thread_2;
328 
329   //printf ("Caller name: %p\n", __builtin_return_address(0));
330   printf("main: %p, fun_1: %p, fun_2: %p\n", main, fun_1, fun_2);
331 
332 
333   struct sigaction sa;
334   sa.sa_handler = sig_handler;
335   sigemptyset(&sa.sa_mask);
336   sa.sa_flags = 0;
337 
338   //signal(SIGUSR1, sig_handler);
339 
340 
341 #if 1
342     //if (sigaction(SIGINT, &sa, NULL) == -1) {
343     if (sigaction(SIGUSR1, &sa, NULL) == -1) {
344         perror("sigaction");
345         exit(EXIT_FAILURE);
346     }
347 #endif
348   sigset_t set;
349   int s;
350 
351   /* Block SIGQUIT and SIGUSR1; other threads created by main()
352      will inherit a copy of the signal mask. */
353 
354   printf("pid: %d\n", getpid());
355 
356 #if 0
357   sigemptyset (&set);
358   //sigaddset (&set, SIGQUIT);
359   sigaddset (&set, SIGUSR1);
360   s = pthread_sigmask (SIG_BLOCK, &set, NULL);
361   if (s != 0)
362     handle_error_en (s, "pthread_sigmask");
363 #endif
364 
365 #if 0
366   s = pthread_create (&thread, NULL, &sig_thread, (void *) &set);
367   if (s != 0)
368     handle_error_en (s, "pthread_create");
369 #endif
370 
371   s = pthread_create (&thread_1, NULL, &fun_1, (void *) &set);
372   if (s != 0)
373     handle_error_en (s, "pthread_create 1");
374 
375 #if 0
376   s = pthread_create (&thread_2, NULL, &fun_2, (void *) &set);
377   if (s != 0)
378     handle_error_en (s, "pthread_create 2");
379 
380   /* Main thread carries on to create other threads and/or do
381      other work */
382 #endif
383   //pause();
384   while(1)
385   {
386     printf("main thread pid: %d\n", getpid());
387     printf("main thread tid: %d\n", gettid());
388     fun_22();
389     #if 0
390     printf("main thread pid: %d\n", getpid());
391     printf("main thread tid: %d\n", gettid());
392     #endif
393   }
394   //pause ();   /* Dummy pause so we can test program */
395 }

這個 __restore_rt function 的內容很簡單, 如 list 5, 一個 system call, 查了一下, 參考 list 6 - rt_sigreturn, man rt_sigreturn 可以知道其內容, 英文不好懂是吧, 讓 chatgpt 翻譯一下, 就容易理解了。

當 kernel 要執行 signal handle 要怎麼做呢? 現在的程式可是執行在 while 的地方, 有什麼辦法可以改到 t1.c L136 void sig_handler(int signo) 執行呢? 不好想吧, 當 t1 process 睡覺時要再次執行之前, kernel 做了手腳, 改動 rip, 讓 t1 process 從 sig_handler 執行, 當然也要在 stack frame 做一些操作, 否則 sig_handler 執行時, 會用到原本的 rsp, 也許會有問題, 一般會建立一個新的 stack frame 讓 sig_handler 在這邊操作 stack, 就不會影響到其他 function 的 stack frame。

當要從 signal handle 回到原本執行的地方 (此例子是回到 while) 要怎麼做呢? 有 2 種作法, signal handle return 時, stack return address 塞 while 的位址, 這樣就回去了。不過 linux kernel 不是這麼做, 用了一個奇特的作法。

一樣是安插了一個 return address, 在 signal handle return 之後會跑去執行 __restore_rt, 然後執行 rt_sigreturn system call 回到 kernel, 這時候再把原本 t1 要執行的 while 回復, 等到下次執行 t1 時, 就從 while 開始執行。

list 5 __restore_rt
1 0000000000405940 <__restore_rt>:
2   405940:       48 c7 c0 0f 00 00 00    mov    $0xf,%rax
3   405947:       0f 05                   syscall
4   405949:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

list 6 https://filippo.io/linux-syscall-table/
%rax	Name	Manual	Entry point
15	rt_sigreturn	rt_sigreturn(2)	sys_rt_sigreturn

至於為什麼自己寫的 backtrace 查不到 fun_123, 簡單來說就是 __restore_rt 的 return address 不是 fun_123, 而是 fun_123 的上一層 fun_12, 自然從 stack frame 找不到 fun_123 這個資訊, 那為什麼 glibc backtrace 找得到呢? 因為它用了別的方法, 下一篇再來介紹這個魔法。

ref:

2024年4月7日 星期日

ff7-reamek mod on steam deck

很久之前就一直很想用 mod, 但都找不到方法, 直到發現以下影片後終於知道要怎麼用 mod。



因為我玩 steam 遊戲幾乎都是用 steam deck, 怕這些 mod 在 steam deck/linux 上很難搞, 還好運氣不錯, 成功在 steam deck/linux 上執行這些 mod, 以下安裝路徑是以 steam deck 為主。影片介紹的是以 windows 路徑為主。

ff7-reamek mod 網址: https://www.nexusmods.com/finalfantasy7remake, 需要註冊才能下載 mod。

FFVIIHook 1.7-74-1-7-1708987498

2022/01/11  下午 06:13             1,480 Engine.ini
2024/02/27  上午 06:23            25,088 xinput1_3.dll
放到 /home/deck/.local/share/Steam/steamapps/common/FINAL FANTASY VII REMAKE/End/Binaries/Win64
(deck@steamdeck Win64)$ ls "/home/deck/.local/share/Steam/steamapps/common/FINAL FANTASY VII REMAKE/End/Binaries/Win64"
Engine.ini  ff7remake_.exe  vkd3d-proton.cache  xinput1_3.dll


mod 檔案放到 /home/deck/.local/share/Steam/steamapps/common/FINAL FANTASY VII REMAKE/End/Content/Paks/

我依照影片建議建立了 ~mods, /home/deck/.local/share/Steam/steamapps/common/FINAL FANTASY VII REMAKE/End/Content/Paks/~mods, 也和影片一樣先用潔西測試, 成功了。建議先照影片流程把
  1. 前置模組: FFVIIHook - INI and dev console unlocker
  2. 潔西模組: Hot Pink Jessie
安裝測試好, 再來玩其他模組。



/home/deck/.local/share/Steam/steamapps/common/FINAL FANTASY VII REMAKE/End/Content/Paks/~mods

我把 ~mods 改成 0-mods 有些 mod 會失效, 所以還是都用 ~mods, 看起來好像是個不成文規定的命名, 有些 mod 還會相依其他 mod, 比較麻煩。

另外也把存檔位置找出來, 30 個存檔不夠用, 只好自己備份。

save files: /home/deck/.local/share/Steam/steamapps/compatdata/1462040/pfx/drive_c/users/steamuser/Documents/My Games/FINAL FANTASY VII REMAKE/Steam/76561198098462161

設定成人 mod:
基本上從這邊進去 site preferences (右上角的按鈕)

Content blocking
Adult content
勾選 Show adult content

[模組MOD] 模組網站nexusmods,開啟成人內容,以及分享其他不重要的小事
[模組MOD] 艾莉絲裸裝 - FFVII-重製版
Tifa 4K Hi-Poly Nude Mod (tifa 裸體)

測試了好多 mod, 最後還是選國王的新衣。



綠意公園 tifa, aerith 那段, 短短2分鐘動畫效果超棒! 同時可以看到 tifa, aerith, 可惜不好放上來。

使用這些遊戲 mod 還蠻有門檻的, 不是無腦就可以搞定, 要花時間查資料。

在 steam deck 上操作不像 windows 方便, 除非你接鍵盤滑鼠外接螢幕又進入桌面模式, 我是用 ssh 登入, 直接打指令操作, 另外可以使用 symbolic link 就不用一直搬移檔案, 如 list 5。

list 5
(deck@steamdeck aerith)$ ls -l
lrwxrwxrwx 1 deck deck 82 Apr  7 09:56 zAerithBunny.pak -> '/home/deck/ff7-org-mods/mods/Aerith Bunny Girl-868-1-0-1649964294/zAerithBunny.pak'

另外由於路徑很長, 用了 symbolic link 縮短路徑, 如 list 6。

list 6
(deck@steamdeck ~)$ ls -l ff7-remake
lrwxrwxrwx 1 deck deck 55 Apr  5 11:17 ff7-remake -> '.steam/steam/steamapps/common/FINAL FANTASY VII REMAKE/'
(deck@steamdeck ~)$ ls -l ff7-paks
lrwxrwxrwx 1 deck deck 28 Apr  5 12:40 ff7-paks -> ff7-remake/End/Content/Paks/
fig 8. ff7-remake mod on steam deck

以下的表情很有趣, cloud 看到 aerith 打扮的很漂亮的表情 (正常男人看到女人裸體應該都是這表情); aerith 看到 cloud 打扮的很漂亮的表情。

fig 5 aerith 在按摩館的打扮 fig 6 看到aerith 在按摩館的打扮後的神情

fig 7 aerith 看到 cloud 女裝的神情 fig 8 cloud 女裝




Bee costume mod 蜜蜂裝也不錯, 玩起來不尷尬。

ref:
【情報】FF7R PC版MOD始動 (2022/06/18 更新STEAM版設定與控制台支援 )

2024年4月3日 星期三

Sweet Baby Inc detected, 遊戲政確推手 Sweet Baby





Sweet Baby 是令遊戲玩家噁心的組織, 簡稱她們 sb 好了, 政治政確這種事情想不到也傳到遊戲產業。還好有《劍星》出來拯救玩家, 希望《劍星》能靠銷售量讓那些搞不清楚的遊戲公司清醒, 不要亂加入政治政確的東西。本來對政治政確的元素沒那麼反感, 現在只要看到遊戲有這些元素, 就不想買了, 不管你的原本設計是這樣還是政治政確的關係 (因為我分不出來是哪種), 玩個遊戲也要這麼累, 真煩。

打著理想的口號做噁心的事情, 還偷偷摸摸隱藏在台面下。

另外也會參考 ref 2 的不推薦遊戲 (有被 SB 染指的遊戲), 列在上面的都不會買, 管你是幾A大作。至少有神鬼寓言4, 最后的生还者2 , 漫威蜘蛛侠2 , 战神5, 消逝的光芒2。

另外懷疑 ff7-remake 以下的部份可能是政治政確。





fig 5. 應該是比較正常的女生角色, 另外2個有點醜化和黑色膚種, 另外是 cloud 女裝, 希望是我自己想太多。不過 cloud 女裝這段其實蠻有趣。

fig 5. 比較正常的女生角色





ref:
  1. 遊戲政確推手Sweet Baby組織不滿被列Steam黑名單,員工出征玩家反引起多方關注
  2. Sweet Baby Inc detected
  3. [新聞] 馬斯克批Sweet Baby是電玩產業的禍害

2024年3月31日 星期日

tpass 購買與設定

「TPASS 行政院通勤月票一卡通」為「行政院促進公共運輸使用方案」, 購買和設定不是很容易, 紀錄一下。

購買方式不是很方便的從那個地點直接購買, 需要預購, 用 ibon 或是上網購買, 也就是要等卡片寄來, 很麻煩, 所以我直接去捷運站購買, 在捷運站服務處可以直接購得, 所以不在捷運站區域的人, 就只能線上購買。

登入會員設定, 需要使用 app 設定這張卡, 還要實名認證, 需要卡片的照片, 身份證的照片, 我之前因為實名問題不太想用, 最後還是因為價錢屈服了。

有各種方案的儲值, 我是用 399 臺鐵方案, 一樣在捷運站服務處處理, 還需要指定一個月的區間, 例如 20240301 ~ 20240331。

為了怕那個步驟搞不定, 我在捷運站完成這3個步驟, 才離開, 然後到臺鐵測試, tpass 可以正常使用。

若是用 app 儲值, 還需要到 ibon 登記卡片的一個月使用區間。目前似乎也可以用手機 NFC 過卡了。

這個補助對固定搭乘大眾交通工具的人幫助很大, 省下不少錢, 以我自己一個月來說大概省 600-399 = 201, 但我算是短程, 長程會省更多。應該也會多鼓勵人們搭乘大眾運輸工具。沒用到的人可能會不太爽, 畢竟是用納稅人的錢補助。
20240420 通過認證, 花了快一個月。

2024年3月21日 星期四

實做 c backtrace

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 內容
0 sp
1call f1 0x7fffffffe058 ret addr 0x4010c0
2 push %rbp (main_rbp) ; f1_rbp=rsp = 0x7fffffffe050 0x7fffffffe050 main_rbp (0x7fffffffe070)
3call f2 0x7fffffffe048 ret addr 0x4010a4
4 push %rbp (f1_rbp) ; f2_rbp=rsp = 0x7fffffffe040 0x7fffffffe040 f1_rbp (0x7fffffffe050)

指令 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) 可以看到類似的作法。

list 6. glibc-2.39/sysdeps/i386/backtrace.c
  1 /* Return backtrace of current program state.
  2    Copyright (C) 1998-2024 Free Software Foundation, Inc.
  3    This file is part of the GNU C Library.
  4
  5    The GNU C Library is free software; you can redistribute it and/or
  6    modify it under the terms of the GNU Lesser General Public
  7    License as published by the Free Software Foundation; either
  8    version 2.1 of the License, or (at your option) any later version.
  9
 10    The GNU C Library is distributed in the hope that it will be useful,
 11    but WITHOUT ANY WARRANTY; without even the implied warranty of
 12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13    Lesser General Public License for more details.
 14
 15    You should have received a copy of the GNU Lesser General Public
 16    License along with the GNU C Library; if not, see
 17    <https://www.gnu.org/licenses/>.  */
 18
 19 #include <execinfo.h>
 20 #include <stdlib.h>
 21 #include <unwind-link.h>
 22
 23 struct trace_arg
 24 {
 25   void **array;
 26   struct unwind_link *unwind_link;
 27   int cnt, size;
 28   void *lastebp, *lastesp;
 29 };
 30
 31 static _Unwind_Reason_Code
 32 backtrace_helper (struct _Unwind_Context *ctx, void *a)
 33 {
 34   struct trace_arg *arg = a;
 35
 36   /* We are first called with address in the __backtrace function.
 37      Skip it.  */
 38   if (arg->cnt != -1)
 39     arg->array[arg->cnt]
 40       = (void *) UNWIND_LINK_PTR (arg->unwind_link, _Unwind_GetIP) (ctx);
 41   if (++arg->cnt == arg->size)
 42     return _URC_END_OF_STACK;
 43
 44   /* %ebp is DWARF2 register 5 on IA-32.  */
 45   arg->lastebp
 46     = (void *) UNWIND_LINK_PTR (arg->unwind_link, _Unwind_GetGR) (ctx, 5);
 47   arg->lastesp
 48     = (void *) UNWIND_LINK_PTR (arg->unwind_link, _Unwind_GetCFA) (ctx);
 49   return _URC_NO_REASON;
 50 }
 51
 52
 53 /* This is a global variable set at program start time.  It marks the
 54    highest used stack address.  */
 55 extern void *__libc_stack_end;
 56
 57
 58 /* This is the stack layout we see with every stack frame
 59    if not compiled without frame pointer.
 60
 61             +-----------------+        +-----------------+
 62     %ebp -> | %ebp last frame--------> | %ebp last frame--->...
 63             |                 |        |                 |
 64             | return address  |        | return address  |
 65             +-----------------+        +-----------------+
 66
 67    First try as far to get as far as possible using
 68    _Unwind_Backtrace which handles -fomit-frame-pointer
 69    as well, but requires .eh_frame info.  Then fall back to
 70    walking the stack manually.  */
 71
 72 struct layout
 73 {
 74   struct layout *ebp;
 75   void *ret;
 76 };
 77
 78
 79 int
 80 __backtrace (void **array, int size)
 81 {
 82   struct trace_arg arg =
 83     {
 84      .array = array,
 85      .unwind_link = __libc_unwind_link_get (),
 86      .size = size,
 87      .cnt = -1,
 88     };
 89
 90   if (size <= 0 || arg.unwind_link == NULL)
 91     return 0;
 92
 93   UNWIND_LINK_PTR (arg.unwind_link, _Unwind_Backtrace)
 94     (backtrace_helper, &arg);
 95
 96   if (arg.cnt > 1 && arg.array[arg.cnt - 1] == NULL)
 97     --arg.cnt;
 98   else if (arg.cnt < size)
 99     {
100       struct layout *ebp = (struct layout *) arg.lastebp;
101
102       while (arg.cnt < size)
103 	{
104 	  /* Check for out of range.  */
105 	  if ((void *) ebp < arg.lastesp || (void *) ebp > __libc_stack_end
106 	      || ((long) ebp & 3))
107 	    break;
108
109 	  array[arg.cnt++] = ebp->ret;
110 	  ebp = ebp->ebp;
111 	}
112     }
113   return arg.cnt != -1 ? arg.cnt : 0;
114 }
115 weak_alias (__backtrace, backtrace)
116 libc_hidden_def (__backtrace)

ref:
誰在呼叫我?不同的backtrace實作說明好文章