During compilation and linking of a simple program, I encountered a weird behavior of gcc
that I cannot explain.
This here works perfectly fine for me
cc -Wall -c -o obj/main.o src/main.c
cc -Wall -c -o obj/foo.o src/foo.c
cc -Wall -o bin/program obj/main.o obj/foo.o
However, once I ask gcc
to create and store dependency information in a separate file, the linker throws an error:
cc -MM -MP -MF deps/main.d -Wall -c -o obj/main.o src/main.c
cc -MM -MP -MF deps/foo.d -Wall -c -o obj/foo.o src/foo.c
cc -Wall -o bin/program obj/main.o obj/foo.o
# obj/main.o: file not recognized: File truncated
# collect2: error: ld returned 1 exit status
I expect gcc
to write the dependency information in the file provided with the -MF statement. In seems that the object files are somehow modified such that the linker cannot read them anymore.
Any suggestions?
Your object files are truncated to 0 bytes as a side-effect of the preprocessor option -MM
Refer to the documentation of GCC's preprocessor options
and you see that -MM
has the same effect as -M
, except that dependency rules are not generated with respect to any system headers
#include
-ed by the translation unit.
For the -M
option, you will see:
Instead of outputting the result of preprocessing, output a rule suitable for make describing the dependencies of the main source file...
And also:
Passing -M to the driver implies -E...
And turning to option -E
you will see:
If you use the -E option, nothing is done except preprocessing.
(That is actually a bit sloppy. Really it should read: "If you use the -E option, no translation is done except preprocessing.")
The -o filename
option specifies that whatever kind of output file is requested - preprocessed code, assembly, object code or binary - it shall be written to filename
.
So, for example, the command
cc -MM -MP -MF deps/main.d -Wall -c -o obj/main.o src/main.c
instructs the compiler (among other things) to:-
-E
).-MM
)obj/main.o
); write to it deps/main.d
instead (by -MF deps/main.d
)As a result, the output file obj/main.o
is clobbered and opened to receive the output of
preprocessing; there is no output of preprocessing; the generated dependency rule is written to
deps/main.d
, and when the command completes obj/main.o
is closed containing 0 bytes. The same
thing happens to obj/foo.o
.
This is not what you want to happen, of course. The options -M
and -MM
are useful only for generating
dependency files independently of compilation. But you want a command to generate a dependency file
deps/<name>.d
and to proceed with translation all the way to object code, output in obj/<name>.o
To do what you want, replace the option -MM
with -MMD
in your commands. In the documentation you'll see:
-MD
-MD is equivalent to -M -MF file, except that -E is not implied... Since -E is not implied, -MD can be used to generate a dependency output file as a side effect of the compilation process.
-MMD
Like -MD except mention only user header files, not system header files.
[my emphasis]