The GCC constructor does NOT start

This question concerns the gcc constructor, the compilation and the link is correct, but it does not start.

There is ac:

UTEST_BEGIN() UID(a_test) { printf("a test"); return true; } UTEST_END(a) 

bc is simlar:

 UTEST_BEGIN() UID(b_test) { printf("b test"); return true; } UTEST_END(b) 

The code object uses UID () to refer to some test functions. My first version adds UTEST_BEGIN () UTEST_END () to enclose UID (), finally I understand that UTEST_BGIN () UTEST_END () is not required, when I change them, I get an unpredictable result.

when I change the definition of UTEST_BEGIN (), UID (), UTEST_END (), I get a different result.

The basic idea is taken from can-i-auto-collect-a-list-of-function-by-c-macro !

Test 1:

 #define UTEST_BEGIN() \ static const bool __m_en = true; \ static struct __uti *__m_uti_head = NULL; bool utest_item_list_add_global(struct __uti *uti); #define UID(f) \ static bool __uti_##f(void); \ __attribute__((constructor)) \ static void uti_construct_##f(void) \ { \ printf("%s\n", #f); \ static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f }; \ utest_item_list_add_global(&__m_uti_##f); \ } \ static bool __uti_##f(void) bool unit_test_item_pump_do(int file_id, bool (*f)(void), const char *f_name); #define UTEST_END(file_name) \ bool unit_test_##file_name(void) \ { \ if (!__m_en) \ return true; \ struct __uti *cur; \ for(cur = __m_uti_head; cur; cur = cur->next) { \ unit_test_set_run_last_line(__LINE__); \ if (!unit_test_item_pump_do(this_file_id, cur->f, cur->f_name)) \ return false; \ } \ return true; \ } 

I got the correct result. I can call __uti_a_test () and __uti_b_test () by reference. In fact, the __uti_xxx () link is NOT implemented with __m_uti_head, so I want to remove UTEST_BEGIN () and UTEST_END ().

run gcc -E ac, continue the macro as:

 static const bool __m_en = 1; static struct __uti *__m_uti_head = ((void *)0); static bool __uti_a_test(void); __attribute__((constructor)) static void uti_construct_a_test(void) { static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" }; utest_item_list_add_global(&__m_uti_a_test); } static bool __uti_a_test(void) { printf("a test"); return 1; } bool unit_test_a(void) { if (!__m_en) return 1; struct __uti *cur; for(cur = __m_uti_head; cur; cur = cur->next) { unit_test_set_run_last_line(19); if (!unit_test_item_pump_do(file_id_a, cur->f, cur->f_name)) return 0; } return 1; } 

Test 2:

 #define UTEST_BEGIN() bool utest_item_list_add_global(struct __uti *uti); #define UID(f) \ static bool __uti_##f(void); \ __attribute__((constructor)) \ static void uti_construct_##f(void) \ { \ printf("%s\n", #f); \ static struct __uti __m_uti_##f = {NULL, this_file_id, __uti_##f, #f }; \ utest_item_list_add_global(&__m_uti_##f); \ } \ static bool __uti_##f(void) #define UTEST_END(file_name) 

The definition of UID () is similar to definition 1. I keep UTEST_BEGIN () and UTEST_END () empty. The compilation and link are correct, but uti_construct_a_test () and uti_construct_b_test () are NOT executed.

run gcc -E ac, continue the macro as:

 static bool __uti_a_test(void); __attribute__((constructor)) static void uti_construct_a_test(void) { static struct __uti __m_uti_a_test = {((void *)0), file_id_a, __uti_a_test, "a_test" }; utest_item_list_add_global(&__m_uti_a_test); } static bool __uti_a_test(void) { printf("a test"); return 1; } 

utest_item_list_add_global () exists in another .c file, the function adds node to the link:

 static struct __uti *m_uti_head = NULL; bool utest_item_list_add_global(struct __uti *uti) { if (NULL == m_uti_head) { m_uti_head = uti; return true; } struct __uti *tail = m_uti_head; while (NULL != tail->next) tail = tail->next; tail->next = uti; return true; } 

Advanced macros seem right. I think the problem is in the link stage, am I right?

+2
source share
1 answer

I found that the gcc attribute ((constructor)) has the following fact:

cons.c is a file containing a constructor function.

  • If only the constructor exists in the cons.c file, compile it as a static library, and then link it to main (), the constructor will ignore it.
  • If any function that is called in main.c exists in cons.c, compile cons.c as a static library, then link it to main (), the constructor will be called before main.
  • If you use "gcc main.c cons.c", the constructor will be called before main.

cons.c:

 #include <stdio.h> static void __attribute__((constructor)) construct_fun(void) { printf("this is a constructor\n"); } void cons(void) { printf("this is cons\n"); } 

test 1:

main.c:

 #include <stdio.h> int main(void) { printf("this is main\n"); } 

compile:

 gcc -c cons.c ar cqs libcon.a cons.o gcc main.c libcon.a 

conclusion: this is the main

test 2:

main.c:

 #include <stdio.h> extern void cons(void); int main(void) { cons(); printf("this is main\n"); } 

compile:

 gcc -c cons.c ar cqs libcon.a cons.o gcc main.c libcon.a 

output:

 this is a constructor this is cons this is main 

test 3:

main.c

 #include <stdio.h> int main(void) { printf("this is main\n"); } 

compile:

 gcc main.c cons.c 

output:

 this is a constructor this is main 

run "gcc -v", output:

Using inline specifications. COLLECT_GCC = NCA COLLECT_LTO_WRAPPER = / USR / libexec / GCC / i686-RedHat-Linux / 4.7.2 / LTO wrapper Purpose: i686-redhat-linux Configured with: ../ configure --prefix = / usr --mandir = / usr / share / man --infodir = / usr / share / info --with-bugurl = http://bugzilla.redhat.com/bugzilla --enable-bootstrap --enable-shared --enable-threads = posix --enable-checking = release --disable-build-with-cxx --disable-build- poststage1-with-cxx --with-system-zlib --enable -__ cxa_atexit --disable-libunwind-exceptions --enable -gnu-unique-object --enable-linker-build-id --with-linker-hash- style = gnu --enable-languages ​​= c, C ++, objc, obj-C ++, java, fortran, ada, go, lto --enable-plugin --enable-initfini-array --enable-java-awt = gtk - -disable-dssi --with-java-home = / usr / lib / jvm / java- 1.5.0-gcj-1.5.0.0 / jre --enable-libgcj-multifile --enable-java-maintainer-mode - c-ecj-jar = / usr / share / java / eclipse-ecj.jar --disable- libjava-multilib --with-ppl --with-cloog --with-tune = generic --with-arch = i686 - -Build = i686-RedHat-Linux Thread Model: posix gcc version 4.7.2 20121109 (Red Hat 4.7.2-8) (GCC)

My question is:

Only the constructor exists in the .c file, compiles it as a static library. Why does gcc ignore the construct? How to avoid this?

+3
source

Source: https://habr.com/ru/post/956238/


All Articles