Keywords: Linux linking error | crt1.o | -nostartfiles option
Abstract: This article delves into a common linking error encountered when porting applications from Solaris to Linux: the undefined reference to 'main' in crt1.o. By analyzing the GCC linker's mechanism, particularly the role of standard startup files like crt1.o, it explains why programs that link successfully on Solaris fail on Linux. The core solution is using the -nostartfiles linker option, which skips linking standard startup files and is suitable for special applications without a main function. The article also discusses alternative approaches, such as the -shared option for creating shared libraries, and provides detailed code examples and implementation steps to help developers understand the underlying principles and resolve the issue effectively.
Problem Background and Error Analysis
When porting applications from Solaris to Linux, developers often encounter a typical linking error: /usr/lib/gcc/x86_64-redhat-linux/4.1.2/../../../../lib64/crt1.o: In function '_start': (.text+0x20): undefined reference to 'main'. This error indicates that the linker cannot find the definition of the main function while attempting to link the standard startup file crt1.o. In Solaris, the same code might compile and link normally due to differences in toolchains and default linking behaviors across operating systems.
GCC Linker and Standard Startup File Mechanism
GCC (GNU Compiler Collection) in Linux environments links standard startup files, such as crt1.o, by default. These files are responsible for setting up the program's initial environment and calling the main function. When an application does not define a main function, the linker reports an undefined reference error. This is common in special-purpose programs, like embedded systems or applications with custom entry points. Below is a simplified example illustrating how standard startup files work:
// Pseudo-code for the _start function in crt1.o
void _start(void) {
// Initialize environment (e.g., set up stack)
initialize_environment();
// Call main function; if undefined, linking fails
int result = main();
// Program exit
exit(result);
}
In Solaris, the linker might not include such startup files by default or use a different entry point mechanism, thus avoiding this error.
Core Solution: Using the -nostartfiles Option
Based on the best answer, the key to resolving this error is using the -nostartfiles linker option. This option instructs GCC not to link the standard system startup files, thereby skipping the linking of crt1.o. It is suitable for applications that implement custom _start code or lack a main function. In the Makefile, this can be modified as follows:
$(LINK) -nostartfiles -g $(RPCAPIOBJ) -o $(RPCAPPN)_server $(IDALIBS) $(LIBS) $(ORALIBS) $(COMMONLIB) $(LIBAPI) $(CCLIB) $(THREADLIB) $(DBSERVERLIB) $(ENCLIB)
From the GCC documentation, -nostartfiles is defined as: "Do not use the standard system startup files when linking. The standard system libraries are used normally, unless -nostdlib or -nodefaultlibs is used." This means standard libraries are still linked, but startup files are excluded, avoiding the undefined main error.
Alternative Solutions and Supplementary References
Another answer suggests using the -shared option to compile the application as a shared library (.so file). Shared libraries do not require a main function as an entry point, thus bypassing this error. For example:
$(LINK) -shared -g $(RPCAPIOBJ) -o lib$(RPCAPPN).so $(IDALIBS) $(LIBS) $(ORALIBS) $(COMMONLIB) $(LIBAPI) $(CCLIB) $(THREADLIB) $(DBSERVERLIB) $(ENCLIB)
However, this approach may alter the application's behavior, as it is no longer a standalone executable but a library that must be loaded by other programs. For porting large legacy systems, -nostartfiles is often a more direct and compatible choice.
Implementation Steps and Code Examples
To clearly demonstrate the solution, we write a simple example program without a main function but with a custom entry point. First, create a C file custom_entry.c:
#include <unistd.h>
#include <stdlib.h>
// Custom entry point function, replacing main
void _start(void) {
// Custom initialization code
write(1, "Hello from custom entry!\n", 26);
// Exit directly to avoid linking main
_exit(0);
}
Then, compile and link using -nostartfiles:
gcc -nostartfiles -o custom_program custom_entry.c
Running ./custom_program will output "Hello from custom entry!" without any linking errors. This demonstrates how to bypass dependency on standard startup files.
Summary and Best Practices
When porting applications across platforms, understanding the target system's linker default behavior is crucial. For the undefined reference to 'main' error in Linux, the -nostartfiles option provides an effective solution by skipping the linking of standard startup files. Developers should assess whether the application requires a main function and consider using custom entry points. In large legacy systems, directly modifying the Makefile to include this option is often the least invasive approach. Meanwhile, the -shared option can serve as an alternative, but its applicability should be noted. By deeply analyzing toolchain mechanisms, developers can handle such porting challenges more flexibly.