gasoline
gasoline

Reputation: 74

g++ fails to link .o files into an executable

I am doing an example drill in the textbook I am using to learn from. All I need to do is compile, link and run the following 3 files:

//file my.h
extern int foo;
void print_foo();
void print(int);

my.h is a simple header file that declares the two functions and a 'global' int foo, with no initial value.

//file my.cpp
#include "my.h"
#include "std_lib_facilities.h" //not included but not source of error

void print_foo()
{
    cout << foo << endl;
}

void print(int i)
{
    cout << i << endl;
}

my.cpp contains the implementation of the functions included from my.h. std_lib_facilities.h is a file from the textbook, and is not the source of error (according to g++). I can edit it into the body of the question if needed.

//file use.cpp
#include "my.h"
#include <iostream>

int main() {
    foo = 7;
    print_foo();
    print(99)

    char cc; cin >> cc;
    return 0;
}

use.cpp serves as the main implementation file in this program, and tries to use all three declared & defined objects.

I took the two step command approach to build using g++. First, I compiled both .cpp files:

g++ -c my.cpp use.cpp

which created two object files, my.o and use.o. I used the following command to link them:

g++ -o myprog my.o use.o

giving me this error:

Undefined symbols for architecture x86_64:
  "_foo", referenced from:
      print_foo() in my.o
      _main in use.o
     (maybe you meant: __Z9print_foov)
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

I have tried putting

int foo;

into my.h instead of

extern int foo;

which gave me the same error.

I have tried using the

-std=c++11

flag as well which resulted in the same error.

I am using a MacBook Pro with the latest macOS (just updated in fact), if that helps with interpreting the error message.

I have tried to initialize foo, which didn't change anything.

In addition, I have tried updating the command line tools, same error.

From what I understand, the error is telling me that, even though my.h is included in both files, neither one can actually implement any function using the foo variable (which it calls _foo), despite it being explicitly declared in my.h. My guess is that the linker is using the wrong names under the hood, which make it impossible to link into an executable. This comes from the fact that the error mentioned a

__Z9print_foov

which exists nowhere in any of the files.

It almost seems like a g++ or macOS/Command Line Tools bug at this point. I don't want to add the declarations each time, because that creates duplicate symbol errors anyway. Putting my.cpp and use.cpp into one file would probably link properly, but I need to make sure that I can actually link multiple cpp files, because I will eventually (hopefully) be working with multiple cpp files that need to be linked. Any help is appreciated!

Upvotes: 2

Views: 3201

Answers (2)

I suggest to compile in two commands g++ -Wall -c my.cpp (that gives a my.o) and g++ -Wall -c use.cpp (giving use.o), then link a program with g++ my.o use.o -o myprog. Actually you should write a Makefile (see this for inspiration) and simply run make

Your translation units my.cpp and use.cpp are both declaring some extern int foo; variable which is never defined. So you need to define it in one single file (but not in others!), probably by adding (into my.cpp alone for example)

 int foo;

(without the extern) or even with some explicit initial value e.g. int foo = 34;

This comes from the fact that the error mentioned a __Z9print_foov which exists nowhere

It is a mangled name, which is referenced (but not defined) in both object files (see also this).

It almost seems like a g++ or macOS/Command Line Tools bug at this point

You are very unlikely to find bugs in compiler tools (both GCC & Clang/LLVM are extremely well tested; since they are multi-million lines free software, they do have residual bugs, but you have more chances to win at the lottery than to be affected by a compiler bug). I'm coding since 1974, and it happened to me only once in my lifetime. A more realistic attitude is to be more humble, and question your own code (and knowledge) before suspecting the compiler or build chain.

BTW, always compile first with all warnings and debug info (e.g. g++ -Wall -g and perhaps also -Wextra). Use the gdb debugger. When you are convinced that your code has no bugs, you might benchmark it by asking the compiler to optimize (so use g++ -Wall -O2 perhaps also with -g to compile).

Read also the linker wikipage. Dive into your C++ textbook (see also this site and the C++11 standard, e.g. n3337 draft) to understand the difference between declaring and defining some variable or function. You generally declare a global extern variable in some common header (included in several translation units), and define it once somewhere else, but the good practice is to avoid having lots of global variables. See also C++17 new inline variables.

Upvotes: 1

M.M
M.M

Reputation: 141554

Here you declare a variable:

extern int foo;

and you use the variable:

cout << foo << endl;

but you did not define the variable anywhere. The linker error says that the linker could not find the variable's definition. To fix this, put int foo; at file scope in one of the .cpp files.

In the question you say that changing extern int foo; to int foo; gives the same error. However if you look more carefully at the error message I think you will find that it gives a different one, about multiple definitions.

Upvotes: 2

Related Questions