我们在学习和编写C程序时,都是从main函数开始,main函数作为入口函数已经深深地印在我们的脑海中,那么main函数真的是C程序的入口函数吗?带着这个问题我们先来看下面一段代码。
#include <stdlib.h>
#include <stdio.h>
static void __attribute__ ((constructor)) beforeMain(void)
{
printf("Before main...\n");
}
int main(void)
{
printf("Main!\n");
return 0;
}
/usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/ccHn29zY.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -z relro /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. /tmp/ccMKdwTx.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/x86_64-linux-gnu/5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014-2015 Free Software Foundation, Inc.
Copying and distribution of this script, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
我们的例子中beforeMain函数使用的gcc扩展属性
__attribute__((constructor))
就是将函数对应的指令归属于.ctors section部分。
.ctors :
{
/* gcc uses crtbegin.o to find the start of
the constructors, so we make sure it is
first. Because this is a wildcard, it
doesn't matter if the user does not
actually link against crtbegin.o; the
linker won't look for a file to match a
wildcard. The wildcard also means that it
doesn't matter which directory crtbegin.o
is in. */
KEEP (*crtbegin.o(.ctors))
KEEP (*crtbegin?.o(.ctors))
/* We don't want to include the .ctor section from
the crtend.o file until after the sorted ctors.
The .ctor section from the crtend file contains the
end of ctors marker and it must be last */
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
KEEP (*(SORT(.ctors.*)))
KEEP (*(.ctors))
}
__attribute__((constructor))
属性 The constructor attribute causes the function to be called automatically before execution enters main (). 构造函数属性使函数在执行进入main()之前自动被调用
__attribute__
机制。__attribute__
可以设置函数属性(Function Attribute )、变量属性(Variable Attribute )和类型属性(Type Attribute )。__attribute__
写法是__attribute__
前后都有两个下划线,并且后面会紧跟一对原括弧,括弧里面是相应的__attribute__
参数。__attribute__
格式为__attribute__((attribute-list))
__attribute__((constructor))
可以让这个函数在main函数执行前运行__attribute__((constructor))
可以提前初始化一些在main函数中用到的东西,便于我们做一些准备工作。我们还可以给属性设置优先级,看下面示例代码
#include <stdlib.h>
#include <stdio.h>
static void __attribute__ ((constructor(101))) beforeMain1(void)
{
printf("Before main...1\n");
}
static void __attribute__ ((constructor(102))) beforeMain2(void)
{
printf("Before main...2\n");
}
static void __attribute__ ((constructor(103))) beforeMain3(void)
{
printf("Before main...3\n");
}
int main(void)
{
printf("Main!\n");
return 0;
}
__attribute__((destructor))
属性 __attribute__((destructor))
,文档中关于这两个用法的说明如下:The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () completes or exit () is called. Functions with these attributes are useful for initializing data that is used implicitly during the execution of the program.
同理, destructor让系统在main()函数退出或者调用了exit()之后,调用我们的函数。
#include <stdlib.h>
#include <stdio.h>
static void __attribute__ ((constructor)) beforeMain(void)
{
printf("Before main...\n");
}
static void __attribute__ ((destructor)) afterMain(void)
{
printf("After main...\n");
}
int main(void)
{
printf("Main!\n");
return 0;
}
__attribute__ ((constructor))
和__attribute__ ((destructor))
类似于C++类中构造函数和析构函数。在main函数之前,执行一个函数,便于我们做一些准备工作;在main()函数退出或者调用了exit()之后调用。多个函数时,GCC为我们提供了一个参数叫优先级,constructor按从小到大,destructor函数相反 void __attribute__((constructor(5)) initFunction1(void); void __attribute__((constructor(10)) initFunction2(void);