如何使其他函数作为C中除main()之外的入口点

时间:2012-03-13 01:52:05

标签: c linux

每个程序都有main(),程序执行从那里开始。是否可以编写没有main()的程序并将另一个函数作为入口点?如果是这样,任何人都可以告诉我该怎么办?我正在使用Linux?

3 个答案:

答案 0 :(得分:5)

如果您正在使用gcc进行编译,则指定-e <symbol>选项将允许您将入口点更改为函数symbol()

答案 1 :(得分:0)

您可以以可移植的方式执行此操作,只需在主程序中包含以下头文件即可:

int nomDePlume(int argc, char *argv[]);
int main(int argc, char *argv[]) { return nomDePlume(argc, argv); }

然后提供您自己的main的变体:

int nomDePlume(int argc, char *argv[]) {
    weaveYourMagic();
    return 42;
}

这实际上并没有改变可执行文件的入口点(这是非常具体的实现),但是确实可以让您随意调用程序入口点(当然,在合法标识符的范围内)。


如果您根本不希望使用main(即使在头文件中),也会有些棘手。

这是因为可执行文件的入口通常不是 main-在调用main之前,有很多活动与环境设置和其他事情(以及main退出后的一些拆解内容,例如您的atexit处理程序)。

换句话说,尽管main函数可能是您的C程序的启动位置,但这与可执行文件的启动位置 不同。

作为一个示例,在Linux中,ELF可执行文件格式在标头块的e_entry字段中包含一个入口点地址,这是程序加载器在准备好运行可执行文件时会跳转到的地址。

>

通常会发生这种情况,是您(隐式地)链接了诸如crt0之类的一些启动代码,并且链接器使用已知符号的地址填充了e_entry字段。一个例子是(从该链接,对注释进行了一些修改):

.text
.globl _start
_start:                       # Entry point known to linker.
    xor %ebp, %ebp            # RBP <- 0, mark end of stack frames.
    mov (%rsp), %edi          # Get argc from stack (implicitly
                              #   zero-extended to 64-bit).
    lea 8(%rsp), %rsi         # Take address of argv from stack.
    lea 16(%rsp,%rdi,8), %rdx # Take address of envp from stack.
    xor %eax, %eax            # Per ABI and compatibility with icc.

    call main                 # Three args %edi/%rsi/%rdx (first
                              #   two are C standard).

    mov %eax, %edi            # Effectively set up _exit(main()).
    xor %eax, %eax            # Per ABI and compatibility with icc.
    call _exit                # Terminate the program.

您可以在main调用的任何一侧看到启动和拆卸事件,这为您提供了一个可能的解决方案。

通过创建自己的自己 crt0,将文本main替换为您要调用的任何内容,并确保链接器使用您的 {{ 1}},而不是默认的,您可以让它调用要提供的任何函数作为主函数。


但是,除了获得工作安全性或在同事找不到您的代码的起始位置时惹恼您的同事之外,能否使用任意命名的main函数究竟能带来什么收益,我们还不清楚>

如果这是有教育意义的,那很好-我只是在现实世界中不会这样做。

答案 2 :(得分:-1)

有一个构建可执行共享库的解决方案,您可以使用另一个函数作为入口点来构建程序。

代码如下:

#include <stdio.h>
#include <stdlib.h>
const char __invoke_dynamic_linker[] __attribute__ ((section (".interp"))) 
    = "/lib/ld-linux.so.2";
void fun()
{
    printf("This is fun./n");
    exit(0);
}

然后将程序构建为共享库,并将func指定为入口点:

$ gcc -fpic -shared -o fun.so -Wl,-e,fun fun.c
$ ./fun.so

这种方式的问题是func不能像main函数那样有正常的参数,这是因为我们没有c库来初始化主要的参数。