Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC constructor NOT execute

Tags:

c

gcc

This question is about gcc constructor, compile & link is right, but it NOT run.

There is a.c:

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

b.c is simlar:

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

The code object is using UID() link some test functions. My first version add UTEST_BEGIN() UTEST_END() to enclose UID(), at last I realize UTEST_BGIN() UTEST_END() isn't necessary, when I change them get unpredicated result.

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

The basic idea come 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 right result. I can call __uti_a_test() and __uti_b_test() through a link. In fact, the __uti_xxx() link is NOT realated with __m_uti_head, so I want to remove UTEST_BEGIN() & UTEST_END().

run gcc -E a.c, the macro extend 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 same as Test 1. I keep UTEST_BEGIN() & UTEST_END() as blank. Compile & Link is right, But uti_construct_a_test() & uti_construct_b_test() NOT execute.

run gcc -E a.c, the macro extend 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;
}

The utest_item_list_add_global() is exist in other .c file, the function add a node into a 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;
}

The expanded macor is seem as right. I think the problem is in link stage, am I right?

like image 955
husthl Avatar asked Sep 17 '26 01:09

husthl


1 Answers

I found gcc attribute((constructor)) have below fact:

cons.c is a file which contain constructor function.

  1. If only constructor is exist in cons.c file, compile it as static library, then link it with main(), the constructor will be ignore.
  2. If any function which is called in main.c is exist in cons.c, compile cons.c as static library, then link it with main(), the constructor will be called before main.
  3. If use "gcc main.c cons.c", 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 by:

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

output is: this is main

test 2:

main.c:

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

compile by:

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 by:

gcc main.c cons.c

output:

this is a constructor
this is main

run "gcc -v", output:

Using built-in specs. COLLECT_GCC=gcc COLLECT_LTO_WRAPPER=/usr/libexec/gcc/i686-redhat-linux/4.7.2/lto-wrapper Target: 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 --with-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 constructor is exist in a .c file, compile it as static library, Why gcc ignore the construct? How to avoid it?

like image 106
husthl Avatar answered Sep 18 '26 14:09

husthl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!