首先c语言程序是否真的需要一个main函数?
double foo()
{
return 1.0;
}
double g=foo();
int main()
{
return0;
}
在main函数之前,程序启动器帮我们初始化好了g这个全局变量。
void foo()
{
printf("foo\n");
}
int main()
{
atexit(&foo);
printf("end\n");
}
这个会打印:
end
foo
程序结束后运行库是不是有动了什么手脚?
还有如果在c语言中不定义一个main,就会出错说找不到main函数,为什么?那能不能换成别的名字?
一个典型的程序的运行步骤:
1. 操作系统在创建进程后,把控制权交给程序的入口,这个入口是运行库中的某一个函数
2. 入口函数对运行库和程序运行的环境进行初始化,堆,I/O,线程,全局变量的构造
3. 入口函数完成后在调用main函数,正式开始主体程序 //到这里才是真的main函数的开始,main函数也是被人家调用的,为什么程序中要有一个main函数,下面就知道了
4. 等main函数完成后回到入口函数,进行清理工作,包括全局变量的结构,堆的销毁,关闭I/O等等,然后结束进程
Glib静态用于可执行文件:
首先注意:参数和环境变量是由连接器压入栈中的,这个在_start前就已经完成了。
入口函数的实现,为什么要写一个main函数???
真正的入口函数,在_start中:
伪代码:
void _start()
{
%ebp=0; xor %ebp %ebp
int argc=pop from stack popl %si
char** argc=top of stack movl %esp,%ecx
__libc_start_main(main,argc,argv,__libc_csu_init,__libc_csu_fini,edx,top of stack);
//这里会调用main函数
}
_start:
xor %ebp,%ebp
pop %esi
mov %esp,%ecx
and $0xfffffff0,%esp
push %eax
push %esp
push %edx
push $__libc_csu_fini
push $__libc_csu_init
push %ecx
push %esi
push $main
call __libc_start_main
_start后的栈的结构:
Stack Top -------------------
main main
-------------------
esi argc
-------------------
ecx argv
-------------------
__libc_csu_init _init
-------------------
__libc_csu_init _fini
-------------------
edx //这个是动态加载的收尾 _rtlf_fini
-------------------
esp stack_end
-------------------
eax this is 0
__libc_start_main的原型:
extern int BP_SYM (__libc_start_main) (int (*main) (int, char **, char **),
int argc,
char *__unbounded *__unbounded ubp_av,
void (*init) (void),
void (*fini) (void),
void (*rtld_fini) (void),
void *__unbounded stack_end)
__attribute__ ((noreturn));
bounded指针:
占用3个指针的空间,第一个空间保存原指针的值,第二个存储下界值,第三个保存上界值
MSVS中的crt入口函数:
主要工作:堆的初始化,I/O的初始化
MSVC 中的IO初始化主要工作:
1. 建立打开列表
2. 如果能够继承,从父进程中继承
3. 初始化标准的输入输出
STATIC int LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
int argc, char *__unbounded *__unbounded ubp_av,
#ifdef LIBC_START_MAIN_AUXVEC_ARG
ElfW(auxv_t) *__unbounded auxvec,
#endif
__typeof (main) init, void (*fini) (void), void (*rtld_fini) (void), void *__unbounded stack_end)
{
#if __BOUNDED_POINTERS__
char **argv;
#else
# define argv ubp_av
#endif
/* Result of the 'main' function. */
int result;
__libc_multiple_libcs = &_dl_starting_up && !_dl_starting_up;
#ifndef SHARED
char *__unbounded *__unbounded ubp_ev = &ubp_av[argc + 1]; //把ubp_ev指向环境变量
INIT_ARGV_and_ENVIRON; //因为有不同版本的bounded,所以定义了这个宏
/* Store the lowest stack address. This is done in ld.so if this is
the code for the DSO. */
__libc_stack_end = stack_end; //栈底
# ifdef HAVE_AUX_VECTOR
/* First process the auxiliary vector since we need to find the
program header to locate an eventually present PT_TLS entry. */
# ifndef LIBC_START_MAIN_AUXVEC_ARG
ElfW(auxv_t) *__unbounded auxvec;
{
char *__unbounded *__unbounded evp = ubp_ev;
while (*evp++ != NULL)
;
auxvec = (ElfW(auxv_t) *__unbounded) evp;
}
# endif
_dl_aux_init (auxvec);
# endif
# ifdef DL_SYSDEP_OSCHECK
if (!__libc_multiple_libcs)
{
/* This needs to run to initiliaze _dl_osversion before TLS
setup might check it. */
DL_SYSDEP_OSCHECK (__libc_fatal);
}
# endif
/* Initialize the thread library at least a bit since the libgcc
functions are using thread functions if these are available and
we need to setup errno. */
__pthread_initialize_minimal (); //线程的初始化工作
/* Set up the stack checker's canary. */
uintptr_t stack_chk_guard = _dl_setup_stack_chk_guard ();
# ifdef THREAD_SET_STACK_GUARD
THREAD_SET_STACK_GUARD (stack_chk_guard);
# else
__stack_chk_guard = stack_chk_guard;
# endif
#endif
/* Register the destructor of the dynamic linker if there is any. */
if (__builtin_expect (rtld_fini != NULL, 1))
__cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL);
#ifndef SHARED
/* Call the initializer of the libc. This is only needed here if we
are compiling for the static library in which case we haven't
run the constructors in `_dl_start_user'. */
__libc_init_first (argc, argv, __environ);
/* Register the destructor of the program, if any. */
if (fini)
__cxa_atexit ((void (*) (void *)) fini, NULL, NULL); //注册main执行完后的清空代码
/* Some security at this point. Prevent starting a SUID binary where
the standard file descriptors are not opened. We have to do this
only for statically linked applications since otherwise the dynamic
loader did the work already. */
if (__builtin_expect (__libc_enable_secure, 0))
__libc_check_standard_fds ();
#endif
/* Call the initializer of the program, if any. */
#ifdef SHARED
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
GLRO(dl_debug_printf) ("\ninitialize program: %s\n\n", argv[0]);
#endif
if (init)
(*init) (argc, argv, __environ MAIN_AUXVEC_PARAM);
#ifdef SHARED
/* Auditing checkpoint: we have a new object. */
if (__builtin_expect (GLRO(dl_naudit) > 0, 0))
{
struct audit_ifaces *afct = GLRO(dl_audit);
struct link_map *head = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
{
if (afct->preinit != NULL)
afct->preinit (&head->l_audit[cnt].cookie);
afct = afct->next;
}
}
#endif
#ifdef SHARED
if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
GLRO(dl_debug_printf) ("\ntransferring control: %s\n\n", argv[0]);
#endif
#ifdef HAVE_CLEANUP_JMP_BUF
/* Memory for the cancellation buffer. */
struct pthread_unwind_buf unwind_buf;
int not_first_call;
not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf);
if (__builtin_expect (! not_first_call, 1))
{
struct pthread *self = THREAD_SELF;
/* Store old info. */
unwind_buf.priv.data.prev = THREAD_GETMEM (self, cleanup_jmp_buf);
unwind_buf.priv.data.cleanup = THREAD_GETMEM (self, cleanup);
/* Store the new cleanup handler info. */
THREAD_SETMEM (self, cleanup_jmp_buf, &unwind_buf);
/* Run the program. */
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
}
else
{
/* Remove the thread-local data. */
# ifdef SHARED
PTHFCT_CALL (ptr__nptl_deallocate_tsd, ());
# else
extern void __nptl_deallocate_tsd (void) __attribute ((weak));
__nptl_deallocate_tsd ();
# endif
/* One less thread. Decrement the counter. If it is zero we
terminate the entire process. */
result = 0;
# ifdef SHARED
unsigned int *ptr = __libc_pthread_functions.ptr_nthreads;
PTR_DEMANGLE (ptr);
# else
extern unsigned int __nptl_nthreads __attribute ((weak));
unsigned int *const ptr = &__nptl_nthreads;
# endif
if (! atomic_decrement_and_test (ptr))
/* Not much left to do but to exit the thread, not the process. */
__exit_thread (0);
}
#else
/* Nothing fancy, just call the function. */
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM); //如果是可可执行文件的话,最关键的是这个
#endif
exit (result);
}
、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
int __tmainCRTStartup(void)
{
int initret;
int mainret=0;
int managedapp;
#ifdef _WINMAIN_
_TUCHAR *lpszCommandLine;
STARTUPINFOW StartupInfo;
GetStartupInfoW( &StartupInfo ); //启动取得信息
#endif /* _WINMAIN_ */
#ifdef _M_IX86
/*
* Enable app termination when heap corruption is detected on
* Windows Vista and above. This is a no-op on down-level OS's
* and enabled by default for 64-bit processes.
*/
if (!_NoHeapEnableTerminationOnCorruption)
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
}
#endif /* _M_IX86 */
/*
* Determine if this is a managed application
*/
managedapp = check_managed_app();
if ( !_heap_init() ) /* initialize heap */ //初始化heap
fast_error_exit(_RT_HEAPINIT); /* write message and die */
if( !_mtinit() ) /* initialize multi-thread */
fast_error_exit(_RT_THREAD); /* write message and die */
/* Enable buffer count checking if linking against static lib */
_CrtSetCheckCount(TRUE);
/*
* Initialize the Runtime Checks stuff
*/
#ifdef _RTC
_RTC_Initialize();
#endif /* _RTC */
/*
* Guard the remainder of the initialization code and the call
* to user's main, or WinMain, function in a __try/__except
* statement.
*/
__try {
if ( _ioinit() < 0 ) /* initialize lowio 初始化IO*/
_amsg_exit(_RT_LOWIOINIT);
/* get wide cmd line info */
_tcmdln = (_TSCHAR *)GetCommandLineT();
/* get wide environ info */
_tenvptr = (_TSCHAR *)GetEnvironmentStringsT();//得到环境变量
if ( _tsetargv() < 0 ) //设置参数
_amsg_exit(_RT_SPACEARG);
if ( _tsetenvp() < 0 ) //设置环境变量
_amsg_exit(_RT_SPACEENV);
initret = _cinit(TRUE); /* do C data initialize c语言库的设置*/
if (initret != 0)
_amsg_exit(initret);
#ifdef _WINMAIN_
lpszCommandLine = _twincmdln();
mainret = _tWinMain( (HINSTANCE)&__ImageBase,
NULL,
lpszCommandLine,
StartupInfo.dwFlags & STARTF_USESHOWWINDOW
? StartupInfo.wShowWindow
: SW_SHOWDEFAULT
);
#else /* _WINMAIN_ */
_tinitenv = _tenviron;
mainret = _tmain(__argc, _targv, _tenviron); //这个才是最后调用的man函数
#endif /* _WINMAIN_ */
if ( !managedapp )
exit(mainret);
_cexit();
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
/*
* Should never reach here
*/
mainret = GetExceptionCode();
if ( !managedapp )
_exit(mainret);
_c_exit();
} /* end of try - except */
return mainret;
}
int __cdecl _heap_init (void)
{
ULONG HeapType = 2;
// Initialize the "big-block" heap first.
if ( (_crtheap = HeapCreate(0, BYTES_PER_PAGE, 0)) == NULL )
return 0;
#ifdef _WIN64
// Enable the Low Fragmentation Heap by default on Windows XP and
// Windows Server 2003. It's the 8 byte overhead heap, and has
// generally better performance charateristics than standard heap,
// particularly for apps that perform lots of small allocations.
if (LOBYTE(GetVersion()) < 6)
{
HeapSetInformation(_crtheap, HeapCompatibilityInformation,
&HeapType, sizeof(HeapType));
}
#endif /* _WIN64 */
return 1;
}
int __cdecl _ioinit (void)
{
STARTUPINFOW StartupInfo;
int cfi_len;
int fh;
int i;
ioinfo *pio;
char *posfile;
UNALIGNED intptr_t *posfhnd;
intptr_t stdfh;
DWORD htype;
GetStartupInfoW( &StartupInfo ); //得到父亲的文件句柄
/*
* Allocate and initialize the first array of ioinfo structs. This
* array is pointed to by __pioinfo[0]
*/
if ( (pio = _calloc_crt( IOINFO_ARRAY_ELTS, sizeof(ioinfo) ))
== NULL )
{
return -1;
}
__pioinfo[0] = pio;
_nhandle = IOINFO_ARRAY_ELTS;
for ( ; pio < __pioinfo[0] + IOINFO_ARRAY_ELTS ; pio++ ) { //进行初始化
pio->osfile = 0;
pio->osfhnd = (intptr_t)INVALID_HANDLE_VALUE;
pio->pipech = 10; /* linefeed/newline char */
pio->lockinitflag = 0; /* uninitialized lock */
pio->textmode = 0;
pio->unicode = 0;
pio->pipech2[0] = 10;
pio->pipech2[1] = 10;
pio->dbcsBufferUsed = FALSE;
pio->dbcsBuffer = '\0';
}
/*
* Process inherited file handle information, if any
*/
if ( (StartupInfo.cbReserved2 != 0) &&
(StartupInfo.lpReserved2 != NULL) ) //如果从父亲那边得到了文件句柄
{
/*
* Get the number of handles inherited.
*/
cfi_len = *(UNALIGNED int *)(StartupInfo.lpReserved2);
/*
* Set pointers to the start of the passed file info and OS
* HANDLE values.
*/
posfile = (char *)(StartupInfo.lpReserved2) + sizeof( int ); //两个指针将划分成,n:表示数量,句柄的属性,句柄数组,见最后图
posfhnd = (UNALIGNED intptr_t *)(posfile + cfi_len);
/*
* Ensure cfi_len does not exceed the number of supported
* handles!
*/
cfi_len = __min( cfi_len, _NHANDLE_ );
/*
* Allocate sufficient arrays of ioinfo structs to hold inherited
* file information.
*/
for ( i = 1 ; _nhandle < cfi_len ; i++ ) { //分配空间
/*
* Allocate another array of ioinfo structs
*/
if ( (pio = _calloc_crt( IOINFO_ARRAY_ELTS, sizeof(ioinfo) ))
== NULL )
{
/*
* No room for another array of ioinfo structs, reduce
* the number of inherited handles we process.
*/
cfi_len = _nhandle;
break;
}
/*
* Update __pioinfo[] and _nhandle
*/
__pioinfo[i] = pio;
_nhandle += IOINFO_ARRAY_ELTS;
for ( ; pio < __pioinfo[i] + IOINFO_ARRAY_ELTS ; pio++ ) {
pio->osfile = 0;
pio->osfhnd = (intptr_t)INVALID_HANDLE_VALUE;
pio->pipech = 10;
pio->lockinitflag = 0;
pio->textmode = 0;
pio->pipech2[0] = 10;
pio->pipech2[1] = 10;
pio->dbcsBufferUsed = FALSE;
pio->dbcsBuffer = '\0';
}
}
/*
* Validate and copy the passed file information
*/
for ( fh = 0 ; fh < cfi_len ; fh++, posfile++, posfhnd++ ) {//copy文件句柄到现在的进程中
/*
* Copy the passed file info iff it appears to describe
* an open, valid file or device.
*
* Note that GetFileType cannot be called for pipe handles
* since it may 'hang' if there is blocked read pending on
* the pipe in the parent.
*/
if ( (*posfhnd != (intptr_t)INVALID_HANDLE_VALUE) &&
(*posfhnd != _NO_CONSOLE_FILENO) &&
(*posfile & FOPEN) &&
((*posfile & FPIPE) ||
(GetFileType( (HANDLE)*posfhnd ) != FILE_TYPE_UNKNOWN)) )
{
pio = _pioinfo( fh );
pio->osfhnd = *posfhnd;
pio->osfile = *posfile;
/* Allocate the lock for this handle. */
if ( !InitializeCriticalSectionAndSpinCount( &pio->lock,
_CRT_SPINCOUNT ))
return -1;
pio->lockinitflag++;
}
}
}
/*
* If valid HANDLE-s for standard input, output and error were not
* inherited, try to obtain them directly from the OS. Also, set the
* appropriate bits in the osfile fields.
*/
for ( fh = 0 ; fh < 3 ; fh++ ) {//初始化标准输入输出,0,1,2
pio = __pioinfo[0] + fh;
if ( (pio->osfhnd == (intptr_t)INVALID_HANDLE_VALUE) ||
(pio->osfhnd == _NO_CONSOLE_FILENO)) {
/*
* mark the handle as open in text mode.
*/
pio->osfile = (char)(FOPEN | FTEXT);
if ( ((stdfh = (intptr_t)GetStdHandle( stdhndl(fh) )) != (intptr_t)INVALID_HANDLE_VALUE) &&
(stdfh!=((intptr_t)NULL)) &&
((htype = GetFileType( (HANDLE)stdfh )) != FILE_TYPE_UNKNOWN) )
{
/*
* obtained a valid HANDLE from GetStdHandle
*/
pio->osfhnd = stdfh;
/*
* finish setting osfile: determine if it is a character
* device or pipe.
*/
if ( (htype & 0xFF) == FILE_TYPE_CHAR )
pio->osfile |= FDEV;
else if ( (htype & 0xFF) == FILE_TYPE_PIPE )
pio->osfile |= FPIPE;
/* Allocate the lock for this handle. */
if ( !InitializeCriticalSectionAndSpinCount( &pio->lock,
_CRT_SPINCOUNT ))
return -1;
pio->lockinitflag++;
}
else {
/*
* For stdin, stdout & stderr, if there is no valid HANDLE,
* treat the CRT handle as being open in text mode on a
* device with _NO_CONSOLE_FILENO underlying it. We use this
* value different from _INVALID_HANDLE_VALUE to distinguish
* between a failure in opening a file & a program run
* without a console.
*/
pio->osfile |= FDEV;
pio->osfhnd = _NO_CONSOLE_FILENO;
}
}
else {
/*
* handle was passed to us by parent process. make
* sure it is text mode.
*/
pio->osfile |= FTEXT;
}
}
/*
* Set the number of supported HANDLE-s to _nhandle
*/
(void)SetHandleCount( (unsigned)_nhandle );
return 0;
}
总结;
运行库:
1. 启动退出
2. 标准函数:由c语言标准规定的标准
3. I/O
4. 堆:初始化,实现封装
5. 语言功能
6. 调试
注意:
1. 不要在不同的CRT中一个申请内存一个释放,因为不能的CRT有不同的堆
2. 不同的CRT中不要在A中打开的file,在B中使用
3. 如果用vs2005等编译的程序在别人电脑上不能使用是因为使用了manifest机制,可以直接使用静态编译,或者和crt一起发布
4. 尽量在同一个crt下开发程序,保存一致性