makefile give error undefined reference to 'main' - c

Makefile:
all: a.out
a.out: b.o a.o
gcc -o b.o a.o
a.o: a.c
gcc -c a.c
b.o: b.c
gcc -c b.c
.PHONY:clean
clean:
rm *.o a.out
with make, give information:
error: undefined reference to 'main'
collect2: ld return 1
make: * [a.out] error 1
But when put source file a.c and b.c into Eclipse CDT, it compiles well.
Please explain what is wrong with my makefile?
PS:
a.c:
int global_1 = 100;
b.c:
extern int global_1;
int main()
{
int global_2 = global_1 * 2;
return 0;
}

This rule doesn't specify the correct result file:
a.out: b.o a.o
gcc -o b.o a.o
It should be
a.out: b.o a.o
gcc -o "$#" b.o a.o
That's what you get for violating one of Paul's Rules of Makefiles. BTW, it's not the makefile that "gives the error", it's the compilation, specifically the linker.

This:
a.out: b.o a.o
gcc -o b.o a.o
\----/
|
|
this says "name
the output b.o"
is overwriting the b.o object file with the output file. I'm pretty sure make is clever enough to figure the required command out without any input, so try deleting the second line.

Related

Why is clang reporting an undefined symbol for an extern variable referenced in a static library

I witnessed this error which is reproducible in AppleClang 12.0.0 but not with gcc 7.5.0. The issue is that I have an extern variable defined in a static library which a different static library wants to use. The linker states that the symbol for the variable is undefined. I tried to come up with an as minimal as possible repro:
externs.h
extern int A;
a.c
int A;
b.h
void bar();
b.c
#include "externs.h"
#include <stdio.h>
void bar()
{
A = 100;
printf("%d\n", A);
}
main.c
#include "b.h"
int main()
{
bar();
}
makefile
liba.a: a.c
gcc -c a.c -o a.o
ar crs liba.a a.o
libb.a: b.c
gcc -c b.c -o b.o
ar crs libb.a b.o
program: main.c liba.a libb.a
gcc main.c -L. -lb -la -o program
On Linux, this program compiles, links and runs without issue. On macOS (where gcc is AppleClang) I get the following output :
gcc -c a.c -o a.o
ar crs liba.a a.o
warning: /Library/Developer/CommandLineTools/usr/bin/ranlib: archive library: liba.a the table of contents is empty (no object file members in the library define global symbols)
gcc -c b.c -o b.o
ar crs libb.a b.o
gcc main.c -L. -lb -la -o program
Undefined symbols for architecture x86_64:
"_A", referenced from:
_bar in libb.a(b.o)
ld: symbol(s) not found for architecture x86_64
AFAICT, the warning is a red herring. If I add a dummy function to a.c, the warning doesn't show. Further, if I inspect liba.a with nm I get the following output:
liba.a(a.o):
0000000000000004 C _A
If I do add a dummy function foo (which doesn't even refer to A) to liba.a and invoke it from main.c, the linking issue gets magically resolved. It's as if if and only if main.o is dependent on a symbol in liba.a, then all the symbols of liba.a become available to anything that may be dependent on them.

Did a Mac update ruin my ability to use gcc?

I am trying to compile two c files into one executable. In the directory I have only three files; Makefile, main.c and myfunction.c.
Makefile:
CC = gcc
CFLAGS = -Wall -g -O0
LIBS = -lm
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
MAIN = main
all: $(MAIN)
#echo Program has been compiled
$(MAIN): $(OBJS)
$(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS) $(LIBS)
clean:
$(RM) *.o *~ $(MAIN)
main.c:
#include <stdio.h>
void myfunc();
int main (int argc, char *argv[]) {
myfunc();
return 0;
}
myfunction.c:
#include <stdio.h>
void myfunc() { printf("hello world"); }
output after make:
gcc -Wall -g -O0 -c -o main.o main.c
gcc -Wall -g -O0 -c -o myfunction.o myfunction.c
gcc -Wall -g -O0 -o main main.o myfunction.o -lm
Undefined symbols for architecture x86_64:
"_myfunc", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [main] Error 1
I had something nearly identical working in the past. I have since clean installed MacOS and updated to Big Sur. Is this the issue or have I overlooked something?
I fixed the issue. I’m not sure what part fixed it, but installed Homebrew and used it to install gcc-10. I also deleted the project and started over.
myfunc would define like file header
myfunc.h
void myfunc()
Declare in another file
myfunc.c
void myfunc() { printf("hello world"); }
Follow the following tutorial
https://developer.gnome.org/anjuta-build-tutorial/stable/build-make.html.en

"File or folder not found" after manually compiling and linking with make

We've got the exercise to create a makefile, which should "create a project bbfoo. bbfoo is linked from barbaz and foo. barbaz is linked from bar and baz. Use the compiler cc (which is gcc on my system) and the linker ld."
I created three simple c files:
bar.c:
int one() {
return 1;
}
baz.c:
int two() {
return 2;
}
foo.c
#include <stdio.h>
int main() {
printf("bar: %d\n", one());
printf("baz: %d\n", two());
return 0;
}
and the following makefile:
bar: bar.c
cc -c -g -o bar.o bar.c
baz: baz.c
cc -c -g -o baz.o baz.c
barbaz: bar baz
ld -shared -o barbaz.o bar.o baz.o
foo: foo.c
cc -c -g -Wno-implicit-function-declaration -o foo.o foo.c
bbfoo: barbaz foo
ld -lc --entry=main -o bbfoo.out barbaz.o foo.o
$ make bbfoo
works without an error and the corresponding files are created. But when I try to run the project with
$ ./bbfoo.out
it displays "File or folder not found".
It works flawless when I use gcc:
$ gcc -o bbfoo.out bar.c baz.c foo.c
$ ./bbfoo.out
bar: 1
baz: 2
What's my mistake? Is it even possible to link a project like this (in two steps)?
Your makefile uses ld to link, but your gcc example uses the compiler driver to link. You should do the same:
bbfoo: barbaz foo
gcc -o bbfoo.out barbaz.o foo.o
The compiler driver may (and in your case clearly does) include a bunch of system-specific stuff that makes your program actually work. Trying to run the linker directly is usually not recommended.
You can use GCC's -v flag if you want to see what the link line it's really using is and copy the flags from that into your makefile if you want, too. But then you're at risk of not picking up important changes in the future.
If you use make you should stick with its basic principle which we can summarize as:
FILE-to-create: files-used-to-create-FILE
shell-commands-that-create-FILE
It is extremely important because make compares last modification times of files to decide what is out of date and what is up to date. It is not a formality and it does change the functionality. So, in your case, you should have:
bar.o: bar.c
cc -c -g -o bar.o bar.c
baz.o: baz.c
cc -c -g -o baz.o baz.c
foo.o: foo.c
cc -c -g -Wno-implicit-function-declaration -o foo.o foo.c
barbaz.so: bar.o baz.o
ld -shared -o barbaz.so bar.o baz.o
bbfoo: barbaz.so foo.o
ld -lc --entry=main -o bbfoo barbaz.so foo.o
Note that make has some nice features that can help factorizing this kind of simple set of rules. Like, for instance automatic variables ($#, $^...), implicit rules (e.g. for building the *.o object files from *.c source files), implicit variables used by the implicit rules (CFLAGS, LD...), target-specific variable values (look at the CFLAGS declaration for foo.o) and many more. Example:
CFLAGS := -g
foo.o: CFLAGS += -Wno-implicit-function-declaration
barbaz.so: bar.o baz.o
$(LD) -shared -o $# $^
bbfoo: barbaz.so foo.o
$(LD) -lc --entry=main -o $# $^
Yes, no rules at all for the object files, make knows already how to build them.
the posted code contains a lot of shortcomings.
Suggest:
bar.h
#ifndef BAR_H
#define BAR_H
int one( void );
#endif
bar.c:
#include "bar.h"
int one() {
return 1;
}
baz.h
#ifndef BAZ_H
#define BAZ_H
int one( void );
#endif
baz.c:
#include "baz.h"
int two() {
return 2;
}
foo.c
#include <stdio.h>
#include "bar.h"
#include "baz.h"
int main( void ) {
printf("bar: %d\n", one());
printf("baz: %d\n", two());
return 0;
}
makefile.mak
.PHONY all
all: bbfoo.out
bar: bar.c bar.h
cc -c -g -o bar.o bar.c -Ibar.h
baz: baz.c baz.h
cc -c -g -o baz.o baz.c -Ibaz.h
libbarbaz.so: baz.o bar.o
ld -shared -o libbarbaz.so bar.o baz.o
foo: foo.c baz.h bar.h
cc -c -g -Wno-implicit-function-declaration -o foo.o foo.c -Ibar.h -Ibaz.h
bbfoo.out: foo.o libbarbaz.so
ld -lc --entry=main -o bbfoo.out foo.o -lbarbaz
Using the short cut variables found in make would greatly shorten this makefile but would make it much more cryptic

Using Assembly global variable in C

Trying to compile this code in a.s:
section .bss
global _start
global TestVar
TestVar: RESB 4
section .text
extern main
_start:
and this code in b.c:
extern int TestVar;
void test2(int x, int y)
{
int z = TestVar;
x = z + y;
y = 1;
}
int main(int argc, char **argv) {
return 0;
}
with this makefile:
all: test
test: a.o b.o
ld -melf_i386 a.o b.o -o test
a.o: a.s
nasm -f elf a.s -o a.o
b.o: b.c
gcc -m32 -Wall -g b.c -o b.o
.PHONY: clean
clean:
rm -f *.o test
running the makefile produces:
m#m-All-Series:~/testFolder$ make
nasm -f elf a.s -o a.o
gcc -m32 -Wall -g b.c -o b.o
/tmp/ccjymll2.o: In function `test2':
/home/m/testFolder/b.c:5: undefined reference to `TestVar'
collect2: error: ld returned 1 exit status
makefile:9: recipe for target 'b.o' failed
make: *** [b.o] Error 1
What am I doing wrong?
also, main is there just because if it isn't - the compiler says there is an undefined reference to main in _start in crt1.o, main will never be called, only test2, I don't know if that matters so I included that info as well.
You must use the compiler's -c option if you want to compile a C source into an object file. Without it, gcc tries to go on linking, which is not what you want. E.g.
b.o: b.c
gcc -c -m32 -Wall -g b.c
should get you a bit further.

Using extern in C doesn't work as expected

I have created two files:
tunables.h
#ifndef TUNABLES_H
#define TUNABLES_H
void tunables_load_conservative();
void tunables_load_aggressive();
extern int timer_x;
#endif /*TUNABLES_H */
and tunables.c
#include "tunables.h"
int timer_x;
void tunables_load_conservative(){
timer_x = 3;
}
void tunables_load_aggressive(){
timer_x = 1;
}
All the other files of my project includes "tunables.h". When I load the project both A.c and B.c calls tunables_load_conservative but if, after a while, I call in file A.c tunables_load_aggressive() in file B.c the timer_x remains 3. Why?
This is my Makefile:
INCLUDE=`pwd`/include
GCCFLAGS= -ansi -O3 -pedantic -Wall -Wunused -I${INCLUDE} -DDEBUG
GCCOTHERFLAGS= -ggdb -pg
all: A B
A: A.o tunables.o
gcc -o A ${GCCFLAGS} ${GCCOTHERFLAGS} tunables.o
B: B.o tunables.o
gcc -o LBfixed ${GCCFLAGS} ${GCCOTHERFLAGS} tunables.o
A.o: A.c
gcc -c ${GCCFLAGS} ${GCCOTHERFLAGS} A.c
B.o: B.c
gcc -c ${GCCFLAGS} ${GCCOTHERFLAGS} B.c
tunables.o: tunables.c
gcc -c ${GCCFLAGS} ${GCCOTHERFLAGS} tunables.c
clean:
rm -rf *.o A B
It looks like you've got two separate processes, A and B. The extern declaration does not set up shared memory across processes, but instead allows different compilation units within the same process to access the same variable.
To share a variable across processes, you will need to use system-dependent IPC methods.

Resources