Undefined reference to "main" in minimal C program - c

I'm trying to understand C compilation in a little more depth, and so I'm compiling and linking "manually". Here is my code
int main()
{
return 0;
}
And here is what I'm putting into my console (Windows):
gcc -S main.c
as main.s -o main.o
ld main.o
And when trying to link, I get:
main.o:main.c:(text+0x7): undefined reference to `__main'

You didn't link any of the necessary support libraries. C global objects like stdin, stdout, stderr don't just appear from nowhere. Command arguments and environment variables are pulled from the operating system. And on exit all those atexit() functions get called and the return code from main is passed to exit(return_code). Etc.
Check out the commands gcc -dumpspecs, gcc -print-libgcc-file-name. Look at all of the other libraries in that directory. You'll find a lot of those libraries and object files referenced in the output of dumpspecs. I don't know exactly when or how those spec rules are interpreted but you can probably get the idea. And I think the GCC info pages info gcc explain it in detail if you dig in far enough.
info gcc and then press 'g' and then enter 'Spec Files'
And as Jonathan Leffler said, the shortcut is to run gcc with the verbose option: gcc -v and just see what commands it used.

Related

gcc ld linker issues [Need Help] [duplicate]

I'm trying to understand C compilation in a little more depth, and so I'm compiling and linking "manually". Here is my code
int main()
{
return 0;
}
And here is what I'm putting into my console (Windows):
gcc -S main.c
as main.s -o main.o
ld main.o
And when trying to link, I get:
main.o:main.c:(text+0x7): undefined reference to `__main'
You didn't link any of the necessary support libraries. C global objects like stdin, stdout, stderr don't just appear from nowhere. Command arguments and environment variables are pulled from the operating system. And on exit all those atexit() functions get called and the return code from main is passed to exit(return_code). Etc.
Check out the commands gcc -dumpspecs, gcc -print-libgcc-file-name. Look at all of the other libraries in that directory. You'll find a lot of those libraries and object files referenced in the output of dumpspecs. I don't know exactly when or how those spec rules are interpreted but you can probably get the idea. And I think the GCC info pages info gcc explain it in detail if you dig in far enough.
info gcc and then press 'g' and then enter 'Spec Files'
And as Jonathan Leffler said, the shortcut is to run gcc with the verbose option: gcc -v and just see what commands it used.

Error while compiling C program with gcc in VSCode

I wanted to compile the below code in VS Code, but I'm fetching this error using "code runner". I've looked up everywhere, but it didn't solve my issue.
I want to implement this T(n) = 2T(n/2) + nlog(n)
q2.c
// b. T(n) = 2T(n/2) + nlog(n)
#include <stdio.h>
#include <math.h>
int func(double n)
{
return (2*func(n/2) + n*(log(n)));
}
int main()
{
double n, result;
printf("Enter the value of 'n' \n");
scanf("%lf",&n);
printf("Hey");
result = func(n);
printf("%lf \n",result);
printf("Hey");
return 0;
}
Console:
user#user-H310M-DS2:~/Desktop/C programming/Assignments$ cd "/home/user/Desktop/C programming/Assignments/" && gcc q2.c -o q2 && "/home/user/Desktop/C programming/Assignments/"q2
/tmp/ccnNXN3L.o: In function `func':
q2.c:(.text+0x3a): undefined reference to `log'
collect2: error: ld returned 1 exit status
Visual studio code has nothing to do with your issue, you are not compiling with it. Because it is an IDE (or source code editor), not a compiler. I guess you are using it on some Linux or POSIX system. BTW my preferred source code editor is GNU emacs. So your IDE is running some compilation commands (and you need to understand which ones and what these commands are doing). You could run these commands in a terminal (and that actually might be simpler).
As your console logs shows, you are compiling with GCC. Some gcc command has been started (by Visual studio code probably).
Read carefully about Invoking GCC. Order of arguments matters a lot!
You should compile your code with
gcc -Wall -Wextra -g q2.c -lm -o q2
Let me explain this a bit:
gcc is your compiler front-end (the actual compiler is cc1 but you never use it directly; you ask gcc to run it)
-Wall asks for almost all warnings
-Wextra asks for extra warnings. You'll be happy to get them
-g asks for debugging information in DWARF. You really want to be able to use the gdb debugger, and gdb practically needs debugging information.
q2.c is the source file of your sole translation unit
-lm is for your math library. You are using log(3) and its documentation mention that.
-o q2 tells gcc to put the executable in q2 (the actual work is done by the ld linker invoked by gcc)
How to configure visual studio code to use that command is your business. You could otherwise type the above command in a terminal. Then you can run your q2 program by typing ./q2 in a terminal for your shell (and you could use gdb on it).
Notice that gcc is starting other programs (like cc1, as, ld). If you want to understand which ones, insert -v after gcc in the command above.
Be sure to read the documentation of every function you are using (so read printf(3), scanf(3), log(3) at least...) and of every program you are using (e.g. of gcc and of Visual studio code).
Once you'll write bigger programs made of several translation units (e.g. foo.c, bar.c, gee.c), you would want to use some build automation tool (because compiling all of them every time with gcc -Wall -Wextra -g foo.c bar.c gee.c -lm -o qqq is possible, but inconvenient). You could learn to use GNU make (or ninja).
Read How to debug small programs. Don't expect your program to work as you want at first.
BTW, study the source code of some existing free software programs (but start with simple projects, e.g. on github, of less than a hundred thousand lines). This could teach you many useful things.
I'm not sure how VSCode compiles programs, but since it uses GCC, it's likely that you need to link the math library libm when compiling, by supplying an argument -lm to GCC.
Just a tweak to code runner's settings.json under file->preferences->settings of VS Code :
I've added the below line
"code-runner.executorMap":
{
"c": "cd $dir && gcc -Wall -Wextra -g $fileName -lm -o $fileNameWithoutExt && $dir$fileNameWithoutExt",
}
It's working now.

Trying to understand the main function with GCC and Windows

They say that main() is a function like any other function, but "marked" as an entry point inside the binary, an entry point that the operating system may find (Don't know how) and start the program from there. So, I'm trying to find out more about this function. What have I done? I created a simple .C file with this code inside:
int main(int argc, char **argv) {
return (0);
}
I saved the file, installed the GCC compiler (in Windows, MingW environment) and created a batch file like this:
gcc -c test.c -nostartfiles -nodefaultlibs -nostdlib -nostdinc -o test.o
gcc -o test.exe -nostartfiles -nodefaultlibs -nostdlib -nostdinc -s -O2 test.o
#%comspec%
I did this to obtain a very simplistic compiler and linker, no library, no header, just the compiler. So, the compiling goes well but the linking stops with this error:
test.c:(.text+0xa): undefined reference to '___main'
collect2.exe: error: Id returned 1 exit status
I thought that the main function is exported by the linker but I believed that you didn't need any library with additional information about it. But it looks like it does. In my case I supposed that it must be the standard GCC library, so I downloaded the source code of it and opened this file: libgcc2.c
Now, I don't know if that is the file where the main function is constructed to be linked by GCC. In fact, I don't understand how the main function is used by GCC. Why does the linker need the gcc standard libraries? To know what about main? I hope this has made my question quite specific and clear. Thanks!
When gcc puts together all object files (test.o) and libraries to form a binary it also prepends a small object (usually crt0.o or crt1.o), which is responsible for calling your main(). You can see what gcc is doing, when you add -v on the command line:
$ gcc -v -o test.exe test.o
crt0/crt1 does some setup and then calls into main. But the linker is finally responsible for building the executable according to the OS. With -v you can see also an option for the target system. In my case it's for Linux 64 bit: -m elf_x86_64. For your system this will be something like -m windows or -m mingw.
The error happens because you use these two options: -nodefaultlibs -nostdlib
These tell GCC that it should not link your code against libc.a/c.lib which contains the code which really calls main(). In a nutshell, every OS is slightly different and most of them don't care about C and main(). Each has their own special way to start a process and most of them are not compatible with the C API.
So the solution of the C developers was to put "glue code" into the C standard library libc.a which contains the interface which the OS expects, creates the standard C environment (setting up the memory allocation structures so malloc() will map the OS's memory management functions, set up stdio, etc) and eventually calls main()
For C developers, this means they get a libc.a for their OS (along with the compiler binaries) and they don't need to care about how the setup works.
Another source of confusion is the name of the reference. On most systems, the symbolic name of main() is _main (i.e. one underscore) while __main is the name of an internal function called by the setup code which eventually calls the real main()

gcc detect duplicate symbols/functions in static libraries

Is there any way we can get gcc to detect a duplicate symbol in static libraries vs the main code (Or another static library ?)
Here's the situation:
main.c erroneously contained a function definition, e.g. with the signature uint foohash(const char*)
foo.c also contains a function definition with the signature uint foohash(const char*)
foo.c and other source files are compiled to a static util library, which the main program links in, i.e. something like:
gcc -o main main.o util.o -L ./libs -lfooutils
So, now main.o and libs/libfooutils.a both contain a foohash function. Presumably the linker found that symbol in main.o and doesn't bother looking for it elsewhere.
Is there any way we can get gcc to detect such a situation ?
Indeed as Simon Richter stated, --whole-archive option can be useful. Try to change your command-line to:
gcc -o main main.o util.o -L ./libs -Wl,--whole-archive -lfooutils -Wl,--no-whole-archive
and you'll see a multiple definition error.
gcc calls the ld program for linking. The relevant ld options are:
--no-define-common
--traditional-format
--warn-common
See the man page for ld. These should be what you need to experiment with to get the warnings sought.
Short answer: no.
GCC does not actually do anything with libraries. It is the task of ld, the linker (called automatically by GCC) to pull in symbols from libraries, and that's really a fairly dumb tool.
The linker has lots of complex jiggery pokery for combining different types of data from different sources, and supporting different file formats, and all the evil little details of binary executables, but in the end, all it really does is look for undefined symbols and find the definitions.
What you can do is a link trace (pass -t to gcc) to see what comes from where. Or else run nm on all the object files and libraries in your system, and write a script to detect duplicates.

Linking with GCC and -lm doesn't define ceil() on Ubuntu

I am currently using GCC to compile and I need to use <math.h>.
The problem is that it won't recognize the library.
I have also tried -lm and nothing.
The function I tried to use was ceil() and I get the following error:
: undefined reference to `ceil'
collect2: ld returned 1 exit status
I am using the latest Ubuntu and math.h is there.
I tried to use -lm on a different computer, and it worked perfectly.
How can I solve this problem?
I did include <math.h>. Also, the command I used was:
gcc -lm -o fb file.c
Take this code and put it in a file ceil.c:
#include <math.h>
#include <stdio.h>
int main(void)
{
printf("%f\n", ceil(1.2));
return 0;
}
Compile it with:
$ gcc -o ceil ceil.c
$ gcc -o ceil ceil.c -lm
One of those two should work. If neither works, show the complete error message for each compilation. Note that -lm appears after the name of the source file (or the object file if you compile the source to object before linking).
Notes:
A modern compiler might well optimize the code to pass 2.0 directly to printf() without calling ceil() at all at runtime, so there'd be no need for the maths library at all.
Rule of Thumb: list object files and source files on the command line before the libraries. This answer shows that in use: the -lm comes after the source file ceil.c. If you're building with make etc, then you typically use ceil.o on the command line (along with other object files); normally, you should list all the object files before any of the libraries.
There are occasionally exceptions to the rule of thumb, but they are rare and would be documented for the particular cases where the exception is expected/required. In the absence of explicit documentation to the contrary, apply the rule of thumb.
I just wanted to mention that Peter van der Linden's book Expert C Programming has a good treatment on this subject in chapter 5 Thinking of Linking.
Archives (static libraries) are acted upon differently than are shared objects (dynamic libraries). With dynamic libraries, all the library symbols go into the virtual address space of the output file, and all the symbols are available to all the other files in the link. In contrast, static linking only looks through the archive for the undefined symbols presently known to the loader at the time the archive is processed.
If you specify the math library (which is usually a static one) before your object files, then the linker won't add any symbols.
Try compiling like that:
gcc -Wall -g file.c -lm -o file
I had the same problem and it was solved using this command. Also if you installed your Ubuntu the same day you had the problem it might be an update problem.

Resources