I have discovered the cause of undefined references. I checked my .o files for symbols, and they were just missing. Just weren't there. For no apparent reason. I have checked the source of the compromiting .o file, but everything seemed to be OK. I've thought that it may be one of those nasty #ifdef ... #endif
blocks, cunningly hidden amongst lines of the code like a bear trap, waiting for its victim. But I couldn't find any. After moving some function definitions to the very end of the file and enclosing them in #ifdef ... #endif
block suitable for Linux, the symbols magically appeared and everything was fine. Thank you for your time.
Outdated, do not read.
I have been assigned to one big project lately, my job is to make it run on Linux platforms. Unfortunately, the programmer who wrote the whole project before me did a very poor job, creating whole lots of circular dependencies by including file
B3D_Base.h
, where almost every header is#included
, to almost every header and source file in the project (that makes 170+ files with#include "B3D_Base.h"
line). This caused no problems during compilation on both Windows and Linux. However, this causes lots of 'undefined reference' errors during linking phase on Linux, even though all object files and libraries are linked.
Being aware of ld
's limitation of single iteration through all object files during linking, I'm asking:
Is there a way to force ld
to do multiple iterations through objects and libraries, possibly solving all the 700+ undefined reference errors, or is my only chance to actually link the project just replacing all the #include "B3D_Base.h"
lines with proper includes to needed headers?
Here is an example of what mess I'm supposed to make run on Linux:
There is one header file B3D_Base.h
, to which is included B3D_Loading3.h
, which contains class B3D_LOADING3
. File B3D_Loading3.cpp
is including only B3D_Base.h
and a memory manager class not included in Base. An instance B3D_LOADING3 g_Loading3;
is declared in B3D_Base.h
and initializes+used in many other parts of the project. Everything compiles just fine, but when it comes to linking, I get many 'undefined reference to g_Loading3'
errors. And there are many more errors like this, complaining about undefined references to both class instances and functions alike. So much for clean code.
I'll be providing an SSCCE example, as kfsone requested:
B3D_Base.h
... (many other includes)
#include "B3D_Loading3.h"
extern B3D_LOADING3 g_Loading3;
... (includes go on)
B3D_Loading3.h/B3D_Loading.cpp
a full definition of class B3D_LOADING3, but no real declaration of g_Loading3
AW_Game.cpp (one of the files where g_Loading3 is used)
#include "B3D_Base.h"
... (some lines)
extern B3D_LOADING3 g_Loading3;
Thanks to kfsone who made me go through the code thoroughly, I managed to find out where the problem lies: g_Loading3 is defined everywhere as extern
, thus not being properly defined. All I needed was to remove the extern
keyword from one file. Fixed me ~ 30 linking errors. Thanks, kfsone.
But there is still problem with undefined references to class functions. After some code scanning, I suppose that the functions are not even defined due to overextended use of #ifdef ... #endif
clauses (portability) and thus it may be my fault, overlooking functions defined only in some passive ifdef block. I will try to look at this issue and later report if anything changed.
APPENDIX #3 REPORT:
Even after properly defining functions, the undefined-reference-list did not shrink. The problem is therefore still open for discussion.
I'll be providing some example code.
Foo.h
#ifndef FOO_H
#define FOO_H
class Foo
{
unsigned int m_size;
bool m_lock;
friend class ASDF;
public:
unsigned int m_id;
void Release() {delete this;}
int Lock(UINT OffsetToLock, UINT SizeToLock, VOID ** ppbData, DWORD Flags);
int Unlock();
};
// other class declarations to follow
#endif
Foo.cpp
#include "Foo.h"
// other includes and function definitions to follow
int Foo::Lock(UINT OffsetToLock, UINT SizeToLock, VOID ** ppbData, DWORD Flags)
{
if(!m_lock)
{
// some locking code
}
return 0;
}
Baz.cpp
#include "Foo.h" // although included, defined and declared, causes undef reference
Foo *m_pFoo;
// several lines later
m_pFoo->Lock(0,0,(void)&Asdf,NULL); // <-- compiles fine, causes undefined reference
One thing worth checking for is determining whether it's a dead code stripping issue. I've had "undefined reference" linker errors in the past because of this.
The MS compiler will remove code if it's not used. GCC will not do this by default, and if that code uses symbols that aren't defined, you'll get errors. Since the MS compiler removes those calls, you get no errors there. In order to see if this is your problem, add these flags to your compilation flags (both for C++ as well as for C, if you also have C sources):
-fdata-sections -ffunction-sections
And this flag to your link flags:
-Wl,--gc-sections
(Of course make sure you're using g++
for linking, not ld
.)
Other than that, my only recommendation is to use a proper build system. I highly recommend CMake. It's easy, reliable, and you should be able to learn it quickly. Creating a CMake-based build for a 300k LOC project with over 100 source files took me less than 3 hours (and at that point it was the first time I ever used CMake.)
Without any kind of example, it's really unclear where your problem might be, except that it's not the ordering of header files - it's what is in those header files and, subsequently, the order in which you link modules.
GCC/LD should only be complaining about undefined references to things that are referenced not just declared.
// t1.h
#pragma once
#include "t2.h"
class Foo1 {};
class Foo3 {}; // This is going to be our undef ref
extern Foo1 g_foo1_a;
extern Foo1& foo1_a();
extern Foo1 g_foo1_b;
extern Foo1& foo1_b();
extern Foo2 g_foo2;
extern Foo3 g_foo3;
// Uncomment this line to generate an undef ref
//static Foo3* g_foo3p = &g_foo3;
// t2.h
#pragma once
#include "t1.h"
class Foo2 {};
extern Foo2 g_foo2;
// t1.cpp
#include "t1.h"
#include "t2.h"
#if defined(_MSC_VER) && _MSC_VER > 9000
Foo3 g_foo3; // gcc won't produce one of these.
#endif
Foo1& foo1_b() { return g_foo1_b; }
int main(int argc, const char** argv)
{
}
// t2.cpp
#include "t1.h"
Foo1 g_foo1_b;
// Makefile
all:
g++ -Wall -o t1.o -c t1.cpp
g++ -Wall -o t2.o -c t2.cpp
g++ -Wall -o t t1.o t2.o
g++ -Wall -o t t2.o t1.o
Compiles without any undef references until you uncomment the static, at which point you get both compile warnings (unused variable) and undefined reference warnings, because nobody actually instantiated g_foo3.
So - while I realize you can't give us your exact code, if you want to resolve the issue you're probably going to need to come up with an SSCCE. Like as not, in the process of replicating the problem, you'll probably figure it out, which is why it's generally a requirement of asking questions here.
On Linux, you will need multiple copies of your -l
arguments unless you want to learn esoteric ld arguments. List all the libs three (yes, 3) times:
-la -lb -lc -la -lb -lc -la -lb -lc
to ensure that all dependencies between the libraries are satisfied. Note that the number 3 is independent of the number of libraries. Some versions of ld
have
-load_all
or -(
as less verbose solutions to this problem.
The reason is that the linker processes undefined symbols right-to-left. When it first sees -la
it only picks up .o files files from liba.a that are required by the base exec. When it sees -lb
it loads those required for the base and liba. If some of liba is required for libb, you're out of luck unless you list liba a second time, etc, etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With