1

I'm trying to write a makefile that goes through compilation step by step, the issue is on the fourth step, linking.

The program consists of multiple files, starting with main.c:

#include "file1.h"
#include "file2.h"

int main() {
  printout1("Print one");
  printout2(5);
  return 0;
}

file1.h

#include <stdio.h>

void printout1(char *str);

file1.c

#include "file1.h"

void printout1(char *str) {
  printf("%s\n", str);
}

file2.h

#include <stdio.h>
#include "file1.h"

void printout2(int);

file2.c

#include "file2.h"

void printout2(int val) {
  printf("%d\n", val);
  printout1("Print two");
}

I'm trying to compile this program with the following makefile:

all:
    cpp main.c main.i 
    gcc -S main.i
    as -o main.o main.s
    ld -o main.o

The following error occurs:

ld: warning: cannot find entry symbol _start; defaulting to 00000000004000b0
main.o: in function 'main':
main.c:(.text+0xa): undefined reference to 'printout1'
main.c:(.text+0x14): undefined reference to 'printout2'
makefile:2 recipe for target 'all' failed
make:***[all] Error 1

I'm fairly new to this sort of thing and I know the problem are the header files, I just don't know how to include them in this.

  • 1.) don't use `ld` directly, use `gcc` as a frontend, 2.) same for `as`, in fact, why would you even want to generate assembly source? 3.) you only ever compile and link your `main` code, so how do you expect the functions from `file1` and `file2` to be available? 4.) `make` is not for writing scripts, use individual rules where the target is *the file created by that rule* –  May 05 '18 at 14:13
  • 2
    where do you compile `file2.c` and `file1.c` (or link them for that matter)? – PeterT May 05 '18 at 14:13
  • 2
    `main.o` calls printout1, but isn't linked with an object file providing it – myaut May 05 '18 at 14:13
  • 2
    Why are you going through assembly? The usual way is to generate object files directly for *all* source files, and then link *all* the object files. – Some programmer dude May 05 '18 at 14:15
  • 1
    Furthermore, when you build with GCC you need a few more object files and libraries, something that the `gcc` frontend program supplies to `ld`, but you don't. – Some programmer dude May 05 '18 at 14:16
  • If it's impossible build it directly is definitely impossible build it step by step. – honzakuzel1989 May 05 '18 at 14:20
  • 1
    Lastly, if you want to go through assembly like that, and use the linker directly, then use the `--verbose` option to `gcc` to see what commands, flags and libraries it uses – Some programmer dude May 05 '18 at 14:20

1 Answers1

4

Two main issues:

  1. gcc is a frontend for all the required steps -- don't try to call the other tools of your toolchain directly, always use gcc. This will make sure it always adds the required options for these other tools.

  2. You have functions in different compilation units (file1 and file2), but never compile and link these.


A sensible Makefile for GNU make with your structure would look like this:

CC ?= gcc

all: main

main: main.o file1.o file2.o
    $(CC) -o$@ $^

%.o: %.c
    $(CC) -c -o$@ $<

.PHONY: all

This compiles all .c files directly to object files, and finally links them (using $(CC), so, e.g. gcc) to your final executable. The rule all is marked as .PHONY because it doesn't create a file named all -- you should let make know about that.

You can of course add additional steps explicitly, but it doesn't make much sense.