Linux executable without main() Function | Write a C Executable program without main() function

Very Short version :

1) Create the program without main() function.

			
rajkumar.r@17:56:58:~/workspace/raj/workouts/ex_without_main$ cat exe_wo_main.c 
#include 
#include 
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
int fn_wo_main() {
	printf("This is a function without main\n");
	exit(0);
}
rajkumar.r@17:57:02:~/workspace/raj/workouts/ex_without_main$

2) Compile it as a Shared object

gcc -shared -Wl,-e,fn_wo_main exe_wo_main.c -o exe_wo_main.so

3) Run it!!

			
rajkumar.r@17:57:36:~/workspace/raj/workouts/ex_without_main$ ./exe_wo_main.so 
This is a function without main
rajkumar.r@17:58:04:~/workspace/raj/workouts/ex_without_main$

Longer Version with Explanation :

Generally, all of the C programs starts at main() function. This is a standard defined fro C programming. When we look at the internals of this, we can understand how the system actually works in the background. C is a compiled language, which means, the source code is converted into the executable, before the execution starts. The general C program compilation process goes through the following steps, which is well documented in several other websites.

C Source code
Preprocessor – Intermediate files
Compiler – Object files
Linker – Linked Object file [ Executable file ]
Loader – Loads the executable and executes.

For our goal, we have to understand the linking process. We write our code starting from main(). But before the main(), there are several things happens in the background. Like, setting up of the environment, fetching input, configuring console etc. These are beautifully abstracted by GCC and the host system which hides those process from the user.

Here, we will unravel a small step of the long and complex process. The entry point of the execution. When the source code is preprocessed and compiled, the object codes are generated. After this, the standard and user built object codes are linked to generate the executable. The linking is done by the linker script which directs how to create the binary executable. This is where, the entry point of the executable is defined and configured as main().

Before main(), there happens few configurations. One among them is, configuration of dynamic loader, which loads the required symbols at run time. Usually in Linux, it is /lib/ld-linux.so.2, which inturn links to the corresponding loader provided by the compiler collection. In my case, it seems to be as below.

rajkumar.r@17:59:50:~/workspace/raj/workouts/ex_without_main$ ls -l /lib/ld-linux.so.2 
lrwxrwxrwx 1 root root 25 Jan 28  2013 /lib/ld-linux.so.2 -> i386-linux-gnu/ld-2.15.so

GCC Compiler is a very powerful and sophisticated compiler. GCC also offers a lot of control to the developers. One among several wonderful option is, -Wl,option. According to gcc man page,

-Wl,option
	   Pass option as an option to the linker.  If option contains commas, it is split
	   into multiple options at the commas.  You can use this syntax to pass an argument
	   to the option.  For example, -Wl,-Map,output.map passes -Map output.map to the
	   linker.  When using the GNU linker, you can also get the same effect with 
	   -Wl,-Map=output.map

From ld manual @t http://ftp.gnu.org/pub/old-gnu/Manuals/ld-2.9.1/html_node/ld_24.html,

The linker command language includes a command specifically for defining the first executable instruction in an output file (its entry point). Its argument is a symbol name:

ENTRY(symbol)

Like symbol assignments, the ENTRY command may be placed either as an independent command in the command file, or among the section definitions within the SECTIONS command--whatever makes the most sense for your layout.

ENTRY is only one of several ways of choosing the entry point. You may indicate it in any of the following ways (shown in descending order of priority: methods higher in the list override methods lower down).

	the `-e' entry command-line option;
	the ENTRY(symbol) command in a linker control script;
	the value of the symbol start, if present;
	the address of the first byte of the .text section, if present;
	The address 0. 

For example, you can use these rules to generate an entry point with an assignment statement: if no symbol start is defined within your input files, you can simply define it, assigning it an appropriate value---

start = 0x2020;

The example shows an absolute address, but you can use any expression. For example, if your input object files use some other symbol-name convention for the entry point, you can just assign the value of whatever symbol contains the start address to start:

start = other_symbol ;

We are going to combine all of these above features to run a program without main function. The steps are..

1) Create the program without main() function.

rajkumar.r@17:56:58:~/workspace/raj/workouts/ex_without_main$ cat exe_wo_main.c 
#include 
#include 
const char my_interp[] __attribute__((section(".interp"))) = "/lib/ld-linux.so.2";
int fn_wo_main() {
	printf("This is a function without main\n");
	exit(0);
}
rajkumar.r@17:57:02:~/workspace/raj/workouts/ex_without_main$

Here, you can see we have a strange thing at line number 2. This is to compensate the missing main() and adding the dynamic loader section. 🙂

2) Compile it as a Shared object

gcc -shared -Wl,-e,fn_wo_main exe_wo_main.c -o exe_wo_main.so

3) Run it!!

rajkumar.r@17:57:36:~/workspace/raj/workouts/ex_without_main$ ./exe_wo_main.so 
This is a function without main
rajkumar.r@17:57:36:~/workspace/raj/workouts/ex_without_main$

Done!!!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s