在c++主函数中,程序的入口点是如何将它转换成其他函数的?

时间:2021-12-07 03:06:42

I was asked an interview question to change the entry point of a C or C++ program from main() to any other function. How is it possible?

有人问我一个面试问题,要我把C或c++程序的入口点从main()改为任何其他函数。怎么可能?

12 个解决方案

#1


40  

In standard C (and, I believe, C++ as well), you can't, at least not for a hosted environment (but see below). The standard specifies that the starting point for the C code is main. The standard (c99) doesn't leave much scope for argument:

在标准C(我相信还有c++)中,您不能这样做,至少在托管环境中不能这样做(但请参阅下面)。该标准指定C代码的起点为main。标准(c99)没有留下太多的争论空间:

5.1.2.2.1 Program startup: (1) The function called at program startup is named main.

5.1.2.2.1程序启动:(1)程序启动时调用的函数名为main。

That's it. It then waffles on a bit about parameters and return values but there's really no leeway there for changing the name.

就是这样。然后它对参数和返回值进行了一些讨论,但是在更改名称的过程中并没有任何回旋余地。

That's for a hosted environment. The standard also allows for a freestanding environment (i.e., no OS, for things like embedded systems). For a freestanding environment:

这是针对托管环境的。该标准还允许独立的环境(例如。没有操作系统,像嵌入式系统)。对于一个独立环境:

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined. Any library facilities available to a freestanding program, other than the minimal set required by clause 4, are implementation-defined.

在独立的环境中(在这种环境中,C程序执行可能没有任何操作系统的好处),在程序启动时调用的函数的名称和类型是由实现定义的。独立程序可用的任何库设施,除了第4条所要求的最小集之外,都是实现定义的。

You can use "trickery" in C implementations so that you can make it look like main isn't the entry point. This is in fact what early Windows compliers did to mark WinMain as the start point.

您可以在C实现中使用“欺骗”,这样您就可以使它看起来像main不是入口点。这实际上是早期Windows编译器将WinMain标记为起点的做法。


First way: a linker may include some pre-main startup code in a file like start.o and it is this piece of code which runs to set up the C environment then call main. There's nothing to stop you replacing that with something that calls bob instead.

第一种方法:链接器可以在像start这样的文件中包含一些主前启动代码。o正是这段代码运行来设置C环境,然后调用main。没有什么可以阻止你用一个叫bob的东西代替它。


Second way: some linkers provide that very option with a command-line switch so that you can change it without recompiling the startup code.

第二种方法:一些链接器提供了带有命令行开关的选项,这样您就可以在不重新编译启动代码的情况下更改它。


Third way: you can link with this piece of code:

第三种方式:你可以链接这段代码:

int main (int c, char *v[]) { return bob (c, v); }

and then your entry point for your code is seemingly bob rather than main.

然后代码的入口点看起来是bob而不是main。


However, all this, while of possibly academic interest, doesn't change the fact that I can't think of one single solitary situation in my many decades of cutting code, where this would be either necessary or desirable.

然而,所有这些,虽然可能是学术上的兴趣,但并不能改变这样一个事实:在我几十年的代码切割过程中,我想不出一个单独的情况,在这种情况下,这要么是必要的,要么是可取的。

I would be asking the interviewer: why would you want to do this?

我会问面试官:你为什么要这样做?

#2


7  

From C++ standard docs 3.6.1 Main Function,

从c++标准文档3.6.1主要功能,

A program shall contain a global function called main, which is the designated start of the program. It is implementation-defined whether a program in a freestanding environment is required to define a main function.

程序应该包含一个名为main的全局函数,即程序的指定开始。是否需要独立环境中的程序来定义主函数,这是实现定义的。

So, it does depend on your compiler/linker...

所以,它依赖于你的编译器/链接器…

#3


7  

The entry point is actually the _start function (implemented in crt1.o) .

入口点实际上是_start函数(在crt1.o中实现)。

The _start function prepares the command line arguments and then calls main(int,char*[]), you can change the entry point from _start to mystart by setting a linker parameter:

_start函数准备命令行参数,然后调用main(int,char*[]),通过设置链接器参数,可以将入口点从_start更改为mystart:

g++ file.o -Wl,-emystart -o runme

Of course, this is a replacement for the entry point _start so you won't get the command line arguments:

当然,这是入口点_start的替换,因此不会得到命令行参数:

void mystart(){

}

#4


6  

If you are on VS2010, this could give you some idea

如果您正在使用VS2010,这可能会给您一些建议

As it is easy to understand, this is not mandated by the C++ standard and falls in the domain of 'implemenation specific behavior'.

因为很容易理解,这不是c++标准要求的,属于“实现特定行为”的范围。

#5


3  

Modify the crt object that actually calls the main() function, or provide your own (don't forget to disable linking of the normal one).

修改实际调用main()函数的crt对象,或者提供自己的(不要忘记禁用正常链接)。

#6


3  

This is highly speculative, but you might have a static initializer instead of main:

这是高度推测性的,但是您可能有一个静态初始化器而不是main:

include

int mymain()
{
    std::cout << "mymain";
    exit(0);
}

static int sRetVal = mymain();

int main()
{
    std::cout << "never get here";
}

You might even make it 'Java-like', by putting the stuff in a constructor:

你甚至可以把这些东西放到构造函数中,使它“像java”:

#include <iostream>

class MyApplication
{
public:
    MyApplication()
    {
        std::cout << "mymain";
        exit(0);
    }
};

static MyApplication sMyApplication;

int main()
{
    std::cout << "never get here";
}

Now. The interviewer might have thought about these, but I'd personally never use them. The reasons are:

现在。面试官可能想过这些,但我个人从来不使用。的原因是:

  • It's non-conventional. People won't understand it, it's nontrivial to find the entry point.
  • 这是非常规。人们不会理解它,找到切入点是很重要的。
  • Static initialization order is nondeterministic. Put in another static variable and you'll never now if it gets initialized.
  • 静态初始化顺序是不确定的。放入另一个静态变量,如果它被初始化,您将永远不会。

That said, I've seen it being used in production instead of init() for library initializers. The caveat is, on windows, (from experience) your statics in a DLL might or might not get initialized based on usage.

也就是说,我看到它被用于生产中,而不是用于库初始化器的init()。需要注意的是,在windows上,(从经验中)您的静态在DLL中可能会或者可能不会根据使用而被初始化。

#7


2  

With gcc, declare the function with attribute((constructor)) and gcc will execute this function before any other code including main.

使用gcc,用属性(构造函数)声明函数,gcc将在包含main的任何其他代码之前执行这个函数。

#8


1  

On windows there is another (rather unorthodox) way to change the entry point of a program: TLS. See this for more explanations: http://isc.sans.edu/diary.html?storyid=6655

windows上还有另一种(相当非传统的)方法来更改程序的入口点:TLS。更多的解释请参见本文:http://isc.sans.edu/diary.html?

#9


1  

For Solaris Based Systems I have found this. You can use the .init section for every platforms I guess:

对于基于Solaris的系统,我发现了这一点。我猜你可以在每个平台上使用。init部分:

   pragma init (function [, function]...)

Source:

来源:

This pragma causes each listed function to be called during initialization (before main) or during shared module loading, by adding a call to the .init section.

这个pragma使每个列出的函数在初始化(main)或共享模块加载过程中被调用,通过向.init部分添加一个调用。

#10


1  

It's very simple:

这是非常简单的:

As you should know when you use constants in c, the compiler execute a kind of 'macro' changing the name of the constant for the respective value.

您应该知道,当您在c中使用常量时,编译器会执行一种“宏”,更改相应值的常量名称。

just include a #define argument in the beginning of your code with the name of start-up function followed by the name main:

只需在代码的开头包含一个#define参数,名称为start函数,名称为main:

Example:

例子:

#define my_start-up_function (main)

#11


1  

I think it is easy to remove the undesired main() symbol from the object before linking.

我认为在链接之前很容易从对象中删除不需要的main()符号。

Unfortunately the entry point option for g++ is not working for me(the binary crashes before entering the entry point). So I strip undesired entry-point from object file.

不幸的是,g++的入口点选项对我不起作用(二进制文件在进入入口点之前崩溃)。因此,我从对象文件中去除不希望的入口点。

Suppose we have two sources that contain entry point function.

假设我们有两个源包含入口点函数。

  1. target.c contains the main() we do not want.
  2. 目标。c包含我们不想要的main()。
  3. our_code.c contains the testmain() we want to be the entry point.
  4. our_code。c包含我们希望作为入口点的testmain()。

After compiling(g++ -c option) we can get the following object files.

编译(g++ -c选项)后,我们可以得到以下对象文件。

  1. target.o, that contains the main() we do not want.
  2. 目标。o,它包含我们不想要的main()。
  3. our_code.o that contains the testmain() we want to be the entry point.
  4. our_code。它包含testmain(),我们希望它是入口点。

So we can use the objcopy to strip undesired main() function.

因此,我们可以使用objcopy来剥离不希望的main()函数。

objcopy --strip-symbol=main target.o

objcopy——strip-symbol =主要target.o

We can redefine testmain() to main() using objcopy too.

我们也可以使用objcopy将testmain()重新定义为main()。

objcopy --redefine-sym testmain=main our_code.o

objcopy——redefine-sym testmain主our_code.o =

And then we can link both of them into binary.

然后我们可以把它们都链接到二进制。

g++ target.o our_code.o -o our_binary.bin

g++目标。o our_code。o - o our_binary.bin

This works for me. Now when we run our_binary.bin the entry point is our_code.c:main() function.

这适合我。现在运行our_binary。入口点是our_code.c:main()函数。

#12


0  

Yes, We can change the main function name to any other name for eg. Start, bob, rem etc.

是的,我们可以把主函数名改成eg的其他名称。首先,鲍勃,快速眼动等。

How does the compiler knows that it has to search for the main() in the entire code ?

编译器如何知道它必须在整个代码中搜索main() ?

Nothing is automatic in programming. somebody has done some work to make it looks automatic for us.

编程中没有什么是自动的。有人已经做了一些工作,使它对我们来说是自动的。

so it has been defined in the start up file that the compiler should search for main().

因此,在启动文件中已经定义了编译器应该搜索main()。

we can change the name main to anything else eg. Bob and then the compiler will be searching for Bob() only.

我们可以把名字改成其他的。然后编译器将只搜索Bob()。

#1


40  

In standard C (and, I believe, C++ as well), you can't, at least not for a hosted environment (but see below). The standard specifies that the starting point for the C code is main. The standard (c99) doesn't leave much scope for argument:

在标准C(我相信还有c++)中,您不能这样做,至少在托管环境中不能这样做(但请参阅下面)。该标准指定C代码的起点为main。标准(c99)没有留下太多的争论空间:

5.1.2.2.1 Program startup: (1) The function called at program startup is named main.

5.1.2.2.1程序启动:(1)程序启动时调用的函数名为main。

That's it. It then waffles on a bit about parameters and return values but there's really no leeway there for changing the name.

就是这样。然后它对参数和返回值进行了一些讨论,但是在更改名称的过程中并没有任何回旋余地。

That's for a hosted environment. The standard also allows for a freestanding environment (i.e., no OS, for things like embedded systems). For a freestanding environment:

这是针对托管环境的。该标准还允许独立的环境(例如。没有操作系统,像嵌入式系统)。对于一个独立环境:

In a freestanding environment (in which C program execution may take place without any benefit of an operating system), the name and type of the function called at program startup are implementation-defined. Any library facilities available to a freestanding program, other than the minimal set required by clause 4, are implementation-defined.

在独立的环境中(在这种环境中,C程序执行可能没有任何操作系统的好处),在程序启动时调用的函数的名称和类型是由实现定义的。独立程序可用的任何库设施,除了第4条所要求的最小集之外,都是实现定义的。

You can use "trickery" in C implementations so that you can make it look like main isn't the entry point. This is in fact what early Windows compliers did to mark WinMain as the start point.

您可以在C实现中使用“欺骗”,这样您就可以使它看起来像main不是入口点。这实际上是早期Windows编译器将WinMain标记为起点的做法。


First way: a linker may include some pre-main startup code in a file like start.o and it is this piece of code which runs to set up the C environment then call main. There's nothing to stop you replacing that with something that calls bob instead.

第一种方法:链接器可以在像start这样的文件中包含一些主前启动代码。o正是这段代码运行来设置C环境,然后调用main。没有什么可以阻止你用一个叫bob的东西代替它。


Second way: some linkers provide that very option with a command-line switch so that you can change it without recompiling the startup code.

第二种方法:一些链接器提供了带有命令行开关的选项,这样您就可以在不重新编译启动代码的情况下更改它。


Third way: you can link with this piece of code:

第三种方式:你可以链接这段代码:

int main (int c, char *v[]) { return bob (c, v); }

and then your entry point for your code is seemingly bob rather than main.

然后代码的入口点看起来是bob而不是main。


However, all this, while of possibly academic interest, doesn't change the fact that I can't think of one single solitary situation in my many decades of cutting code, where this would be either necessary or desirable.

然而,所有这些,虽然可能是学术上的兴趣,但并不能改变这样一个事实:在我几十年的代码切割过程中,我想不出一个单独的情况,在这种情况下,这要么是必要的,要么是可取的。

I would be asking the interviewer: why would you want to do this?

我会问面试官:你为什么要这样做?

#2


7  

From C++ standard docs 3.6.1 Main Function,

从c++标准文档3.6.1主要功能,

A program shall contain a global function called main, which is the designated start of the program. It is implementation-defined whether a program in a freestanding environment is required to define a main function.

程序应该包含一个名为main的全局函数,即程序的指定开始。是否需要独立环境中的程序来定义主函数,这是实现定义的。

So, it does depend on your compiler/linker...

所以,它依赖于你的编译器/链接器…

#3


7  

The entry point is actually the _start function (implemented in crt1.o) .

入口点实际上是_start函数(在crt1.o中实现)。

The _start function prepares the command line arguments and then calls main(int,char*[]), you can change the entry point from _start to mystart by setting a linker parameter:

_start函数准备命令行参数,然后调用main(int,char*[]),通过设置链接器参数,可以将入口点从_start更改为mystart:

g++ file.o -Wl,-emystart -o runme

Of course, this is a replacement for the entry point _start so you won't get the command line arguments:

当然,这是入口点_start的替换,因此不会得到命令行参数:

void mystart(){

}

#4


6  

If you are on VS2010, this could give you some idea

如果您正在使用VS2010,这可能会给您一些建议

As it is easy to understand, this is not mandated by the C++ standard and falls in the domain of 'implemenation specific behavior'.

因为很容易理解,这不是c++标准要求的,属于“实现特定行为”的范围。

#5


3  

Modify the crt object that actually calls the main() function, or provide your own (don't forget to disable linking of the normal one).

修改实际调用main()函数的crt对象,或者提供自己的(不要忘记禁用正常链接)。

#6


3  

This is highly speculative, but you might have a static initializer instead of main:

这是高度推测性的,但是您可能有一个静态初始化器而不是main:

include

int mymain()
{
    std::cout << "mymain";
    exit(0);
}

static int sRetVal = mymain();

int main()
{
    std::cout << "never get here";
}

You might even make it 'Java-like', by putting the stuff in a constructor:

你甚至可以把这些东西放到构造函数中,使它“像java”:

#include <iostream>

class MyApplication
{
public:
    MyApplication()
    {
        std::cout << "mymain";
        exit(0);
    }
};

static MyApplication sMyApplication;

int main()
{
    std::cout << "never get here";
}

Now. The interviewer might have thought about these, but I'd personally never use them. The reasons are:

现在。面试官可能想过这些,但我个人从来不使用。的原因是:

  • It's non-conventional. People won't understand it, it's nontrivial to find the entry point.
  • 这是非常规。人们不会理解它,找到切入点是很重要的。
  • Static initialization order is nondeterministic. Put in another static variable and you'll never now if it gets initialized.
  • 静态初始化顺序是不确定的。放入另一个静态变量,如果它被初始化,您将永远不会。

That said, I've seen it being used in production instead of init() for library initializers. The caveat is, on windows, (from experience) your statics in a DLL might or might not get initialized based on usage.

也就是说,我看到它被用于生产中,而不是用于库初始化器的init()。需要注意的是,在windows上,(从经验中)您的静态在DLL中可能会或者可能不会根据使用而被初始化。

#7


2  

With gcc, declare the function with attribute((constructor)) and gcc will execute this function before any other code including main.

使用gcc,用属性(构造函数)声明函数,gcc将在包含main的任何其他代码之前执行这个函数。

#8


1  

On windows there is another (rather unorthodox) way to change the entry point of a program: TLS. See this for more explanations: http://isc.sans.edu/diary.html?storyid=6655

windows上还有另一种(相当非传统的)方法来更改程序的入口点:TLS。更多的解释请参见本文:http://isc.sans.edu/diary.html?

#9


1  

For Solaris Based Systems I have found this. You can use the .init section for every platforms I guess:

对于基于Solaris的系统,我发现了这一点。我猜你可以在每个平台上使用。init部分:

   pragma init (function [, function]...)

Source:

来源:

This pragma causes each listed function to be called during initialization (before main) or during shared module loading, by adding a call to the .init section.

这个pragma使每个列出的函数在初始化(main)或共享模块加载过程中被调用,通过向.init部分添加一个调用。

#10


1  

It's very simple:

这是非常简单的:

As you should know when you use constants in c, the compiler execute a kind of 'macro' changing the name of the constant for the respective value.

您应该知道,当您在c中使用常量时,编译器会执行一种“宏”,更改相应值的常量名称。

just include a #define argument in the beginning of your code with the name of start-up function followed by the name main:

只需在代码的开头包含一个#define参数,名称为start函数,名称为main:

Example:

例子:

#define my_start-up_function (main)

#11


1  

I think it is easy to remove the undesired main() symbol from the object before linking.

我认为在链接之前很容易从对象中删除不需要的main()符号。

Unfortunately the entry point option for g++ is not working for me(the binary crashes before entering the entry point). So I strip undesired entry-point from object file.

不幸的是,g++的入口点选项对我不起作用(二进制文件在进入入口点之前崩溃)。因此,我从对象文件中去除不希望的入口点。

Suppose we have two sources that contain entry point function.

假设我们有两个源包含入口点函数。

  1. target.c contains the main() we do not want.
  2. 目标。c包含我们不想要的main()。
  3. our_code.c contains the testmain() we want to be the entry point.
  4. our_code。c包含我们希望作为入口点的testmain()。

After compiling(g++ -c option) we can get the following object files.

编译(g++ -c选项)后,我们可以得到以下对象文件。

  1. target.o, that contains the main() we do not want.
  2. 目标。o,它包含我们不想要的main()。
  3. our_code.o that contains the testmain() we want to be the entry point.
  4. our_code。它包含testmain(),我们希望它是入口点。

So we can use the objcopy to strip undesired main() function.

因此,我们可以使用objcopy来剥离不希望的main()函数。

objcopy --strip-symbol=main target.o

objcopy——strip-symbol =主要target.o

We can redefine testmain() to main() using objcopy too.

我们也可以使用objcopy将testmain()重新定义为main()。

objcopy --redefine-sym testmain=main our_code.o

objcopy——redefine-sym testmain主our_code.o =

And then we can link both of them into binary.

然后我们可以把它们都链接到二进制。

g++ target.o our_code.o -o our_binary.bin

g++目标。o our_code。o - o our_binary.bin

This works for me. Now when we run our_binary.bin the entry point is our_code.c:main() function.

这适合我。现在运行our_binary。入口点是our_code.c:main()函数。

#12


0  

Yes, We can change the main function name to any other name for eg. Start, bob, rem etc.

是的,我们可以把主函数名改成eg的其他名称。首先,鲍勃,快速眼动等。

How does the compiler knows that it has to search for the main() in the entire code ?

编译器如何知道它必须在整个代码中搜索main() ?

Nothing is automatic in programming. somebody has done some work to make it looks automatic for us.

编程中没有什么是自动的。有人已经做了一些工作,使它对我们来说是自动的。

so it has been defined in the start up file that the compiler should search for main().

因此,在启动文件中已经定义了编译器应该搜索main()。

we can change the name main to anything else eg. Bob and then the compiler will be searching for Bob() only.

我们可以把名字改成其他的。然后编译器将只搜索Bob()。