2012年8月23日 星期四

c++ runtime - global object ctor (2) - 透過 elf section 來呼叫 global ctor

如果你有看之前的 c++ runtime - global object ctor (0), (1) 應該知道 g++ 會為 global object constructor 準備一個 _GLOBALxxx 的 function。只要 call 它, 就會完成所有 global object ctor 的呼叫。

g++ 4.4, g++ 4.6 將 _GLOBAL__sub_I_ab 放在 .ctor section
g++ 4.7 將 _GLOBAL__sub_I_ab 放在 .init_array section

g++ 4.6
[ 2] .ctors PROGBITS 000008ac 0008ac 000004 00 WA 0 0 4

g++ 4.7.1
[ 2] .init_array INIT_ARRAY 00000938 000938 000004 00 WA 0 0 4

運用類似 init bss 的技巧, 我們在 linker script (cpp.ld) 插入 symbol, L12 ~ L24, 只要其中一段就可以, 不過我為了支援 g++ 4.4, g++4.6, g++ 4.7 所以補了兩段。

但是我不知道 g++ 4.7.1 的 _GLOBAL__sub_I_ab 放在那個 section, 無法使用 L21 的指令, 只好用 L13 ~ L17 這樣有點遜的方法來處理。

cpp.ld
 1 /* for cb.c */
 2 ENTRY(_start);
 3 SECTIONS
 4 {
 5 
 6     . = 0x100;
 7     .text :
 8     {
 9         *(.text)
10         *(.gnu.linkonce.t*)
11     }
12     /* for g++ 4.7 */
13     .init_array :
14     {
15       __start_global_ctor__ = .;
16     }
17     __end_global_ctor__ = .;
18     .ctors :
19     {
20       start_ctors = .; _start_ctors = .; __start_ctors = .;
21       *(.ctor*)
22       end_ctors = .; _end_ctors = .; __end_ctors = .;
24      }
25      /*
26     .dtors :
27     {
28       start_dtors = .; _start_dtors = .; __start_dtors = .;
29       *(.dtor*)
30       end_dtors = .; _end_dtors = .; __end_dtors = .;
31       . = ALIGN(0x1000);
32      }
33      */
34 
35     .rodata :
36     {
37         *(.rodata*)
38         *(.gnu.linkonce.r*)
39     }
40 
41     .data :
42     {
43         *(.data)
44         *(.gnu.linkonce.d*)
45     }
46 
47     .bss :
48     {
49         sbss = .; __bss_start__ = .;
50         *(COMMON)
51         *(.bss)
52         *(.gnu.linkonce.b*)
53         ebss = .; __bss_end__ = .;
54     }
55 
56     /DISCARD/ :
57     {
58         *(.comment)
59         *(.eh_frame) /* discard this, unless you are implementing 
                             runtime support for C++ exceptions. */
60     }
61 }

110 # call global object ctor
111 init_cpp_global_asm:
112 #if __GNUC__ >= 4 && __GNUC_MINOR__ >= 7
113   mov $__end_global_ctor__, %edi    /* Destination */
114   mov $__start_global_ctor__, %esi   /* Source */
115 #else
116   mov $__end_ctors, %edi    /* Destination */
117   mov $__start_ctors, %esi   /* Source */
118 #endif
119   jmp 2f
120 1:
121   mov %esi, %ebx 
122   calll *(%ebx)
123   add $4, %esi
124 2:
125   cmp %edi, %esi
126   jne 1b
127   ret

init_cpp_global_asm 依序呼叫 linker script symbol __end_global_ctor__, __start_global_ctor__ 之間的位址, 不過在這例子中:

00000944 A __end_global_ctor__
00000940 T __start_global_ctor__

所以只有一個 4byte 位址, 4e 04 00 00 => 0x0000044e (_GLOBAL__sub_I_ab),
如果你忘記這怎麼來的請參考:  c++ runtime - global object ctor (0)
 
122   calll *(%ebx)
呼叫它就對了。at &t call 指令語法很奇怪, 別擔心, 常看就會習慣了。

寫成下面的程式碼也可以:

121   mov $__start_global_ctor__, %ebx 
122   calll *(%ebx)

這樣應該比較好懂, 沒有惱人的 loop, 不過你得知道這樣會有什麼風險, 能看懂這篇的朋友我相信應該有能力處理這小問題。也應該換你們踩踩地雷, 保證印象深刻。

 82   calll init_cpp_global_asm
 83   calll cpp_main

最後在 c++ main 之前呼叫 init_cpp_global_asm (cpp_main) 就完成 global object constructor 的呼叫。global object constructor 的秘密到此結束, 再來我會談談 global object destructor。
 
cpp_init.S
  1 # init c++ runtime
  2 
  3 #define DOS
  4 
  5 # How to make G++ preprocessor output a newline in a macro?
  6 # ref: http://stackoverflow.com/questions/2271078/how-to-make-g-preprocessor-output-a-newline-in-a-macro
  7 #define PRINT_REG(REG)  /*
  8 */  pushl $REG /*
  9 */  calll _Z9print_strPKc /*
 10 */  addl $4, %esp /*
 11 */  pushl $16 /*
 12 */  pushl %REG /*
 13 */  calll _Z13s16_print_intii /*
 14 */  addl $8, %esp /*
 15 */  pushl $CRLF /*
 16 */  calll _Z9print_strPKc /*
 17 */  addl $4, %esp
 18 
 19 #define PRINT_VAR(VAR)  /*
 20 */  pushl $var /*
 21 */  calll _Z9print_strPKc /*
 22 */  addl $4, %esp /*
 23 */  pushl $16 /*
 24 */  pushl VAR /*
 25 */  calll _Z13s16_print_intii /*
 26 */  addl $8, %esp /*
 27 */  pushl $CRLF /*
 28 */  calll _Z9print_strPKc /*
 29 */  addl $4, %esp
 30 .code16
 31 .extern __bss_start__
 32 .extern __bss_end__
 33 .extern __start_ctors
 34 .extern __end_ctors
 35 .extern cpp_main
 36 .extern obj_count
 37 
 38 
 39 .text
 40   jmp _start
 41 .global _start
 42 _start:
 43   xchg %bx, %bx
 44 #if 1
 45   mov %cs, %ax
 46   mov %ax, %ds
 47   mov %ax, %ss
 48 
 49 #endif

 73 
 74   call init_bss_asm # in dos need not init bss by myself
 75 
 76 
 77   mov obj_count, %bx
 78   calll disp_bx
 79 
 80   # call _GLOBAL__I_XX invoke global object ctor, 
 81   # in dos environment use 16 or 32 bit address? look like 32 bit
 82   calll init_cpp_global_asm
 83   calll cpp_main
 84   calll g_dtors

 86   mov     $0x4c00, %ax
 87   int     $0x21   # 回到 DOS
 88 

109 
110 # call global object ctor
111 init_cpp_global_asm:
112 #if __GNUC__ >= 4 && __GNUC_MINOR__ >= 7
113   mov $__end_global_ctor__, %edi    /* Destination */
114   mov $__start_global_ctor__, %esi   /* Source */
115 #else
116   mov $__end_ctors, %edi    /* Destination */
117   mov $__start_ctors, %esi   /* Source */
118 #endif
119   jmp 2f
120 1:
121   mov %esi, %ebx 
122   calll *(%ebx)
123   add $4, %esi
124 2:
125   cmp %edi, %esi
126   jne 1b
127   ret
128 
129 # init bss
130 init_bss_asm:
131   movw $__bss_end__, %di    /* Destination */
132   movw $__bss_start__, %si   /* Source */
133   movw %ds, %bx
134   movw %bx, %es
135   jmp 2f
136 1:
137   mov $0, %eax
138   movw %si, %ax
139   movb $0x0, %es:(%eax)
140   add $1, %si
141 

146 
147   
148 2:
149   cmpw %di, %si
150   jne 1b
151 
152   ret
153 

178 
179 disp_al:
180         pushl   %ebx
181         pushl   %ecx
182         pushl   %edx
183 
184         movl (cur_pos), %ebx
185         movb    $0x0c, %ah # 0000: 黑底    1100: 紅字
186         movb    %al, %dl
187         shr     $4, %al
188         movl    $2, %ecx
189 .begin_1:
190         andb    $0x0f, %al
191         cmp     $9, %al
192         ja      .31          # cf=0, zf=0, above 9 (>9)
193         #addb   $'0', %al
194         addb    $0x30, %al
195         jmp     .41
196 .31:
197         sub     $0x0A, %al
198         #add    $'A', %al
199         add     $0x41, %al
200 .41:

208         movw    %ax, %gs:(%ebx)

210         add     $2, %ebx
211 
212         mov %ebx, cur_pos
213 
214         mov     %dl, %al
215         loop    .begin_1
216         addl    $2, %ebx
217 
218         popl    %edx
219         popl    %ecx
220         popl    %ebx
221 
222         ret
223 
224 disp_bx:
225   pushl %eax
226   mov %bx, %ax
227   shr $8, %ax
228   call disp_al
229   mov %bl, %al
230   call disp_al
231   popl %eax
232   ret
233 
234 .data
235 CRLF: .asciz "\r\n"
236 cs: .asciz "%CS: "
237 ds: .asciz "%DS: "
238 ss: .asciz "%SS: "
239 var: .asciz "var: "
240 cur_pos: .int 0

沒有留言:

張貼留言

使用 google 的 reCAPTCHA 驗證碼, 總算可以輕鬆留言了。

我實在受不了 spam 了, 又不想讓大家的眼睛花掉, 只好放棄匿名留言。這是沒辦法中的辦法了。留言的朋友需要有 google 帳號。