Unsdefined reference to library class members errors from caller program
Additional Questions added below, 4/11/2011
I am developing a cross-platform set of shared libraries DLLs/Sos and tester programs in C++ though I have to be able to support C. The libraries will ship as object code only, but the tester program(s) will ship with source so our customers can have example code. For this reason I am designing the libraries to be loaded at runtime, i.e. dynamic linking using dlopen()/LoadLibraryA().
I am using g++ 4.4.3-4 on Umbutu 10.04 and VC++ 2008 on Vista/64 (in 32 bit mode).
Everything seems to work just fine on Windows (right now). However, when I compile on Linux I am getting some errors I can't figure out.
The tester and the library have several classes coded in several .cpp's and .h's. The classes and most everything in the library except the main entry points are in a namespace DISCOVER_NS.
Here is a brief sketch of the project:
First, an admission, I've shortened a bunch of names so the code is more readable.
discover.cpp
Creates a class object with a pointer to it called theMainObject of type DiscoverObject.
Has an extern "C" function that returns theMainObject to the caller program as void*.
DiscoverObject has several methods and instantiates other classes found in separate cpp's and .h's. One particular method is named Hello(), which does what you'd expect, it prints a "hello" test message..
tester.cpp
Gets a handle to the library
gets the function pointer to the function that returns theMainObject.
Executes the function (pointer) and casts the returned address from void* to DISCOVER_NS::DiscoverObject* aDiscoverObject.
Run aDiscoverObject->Hello().
I compile with:
CC = @g++
gflags = -g3
cflags = -fPIC -Wall -pedantic
lib_linkflags := -shared -fPIC -lstdc++ -lrt -lpthread -rdynamic
tester_linkflags := -ldl -lpthread
defines = -D_linux_ -D_DEBUG -D_IPC_ARCH_INTEL=1 -D_THREAD_SAFE
Now, when I compile I get these errors: *Tester.cpp:142: undefined reference to `Discover_NS::DiscoverObject::hello()'*
I also get a bunch of other undefined reference errors from discover.so saying, for example: *discover.so: undefined reference to `Discover_NS::DeviceList::~DeviceList()*
I have tried making virtually everything in the SO extern "C". No difference.
I tried putting statements into discover.cpp that look like: extern void Discover_NS::OtherClass::method( args ); but that gives me errors about" declaration outside of class is not deffinition" errors.
I know it will help to see code, but I need time to whack out something small for posting.
Can anyone offer ideas to solve this mess?
Thanks,
Wes
Dmitry's solution was not quite all of the fix, but was a necessary element in the solution. Upon examination of my makefile I found a couple of unintentionally duplicated lines, which I removed, and two "typos" where I had the wrong path for -o's coded into compile steps. The broken steps compiled logger.cpp and RemException.cpp:
./common/logger.o : ./common/logger.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common
-I ./EdgeIO -I ./Discover
-o ./common/Debug/logger.o <+++++++++ path to .o was wrong
./common/logger.cpp 2>&1 | tee ./RemKonTester/logger.ERR
Then I found the real bug. I completely missed the fact that I wasn't compiling all of my .cpp's in the Discover directory!. It took a good hour to get all the nit-pics removed but now she comiles from a makefile just fine.
NEW VERSION OF THE ORIGINAL QUESTION: NOw that I know it WILL work via makefile, how do I talk Eclipse into doing the same thing the makefile is doing?
Thanks Dmitry.
Wes
Well, my problem is still here.
I have my code compiling with Dmitry's (@Dmitry) suggestions in place. Only, they appear to be causing a separate problem. I want my libraries to link to he main test program dynamically, at run time. Adding the -l Discover -l EdgeIO to the link gets everything to compile, but it give me static linking.
FYI, The unused "pi"s are so the SO has a floating point number in it and will thus compile with floating point support. Required if caller ever wants to use floating point numbers. Anybody got a better way to force g++ to comkpile with floating point included?
After fixing the many bugs Dmitry helped me find, I now get this output:
make
./Discover/dllmain.cpp: In function ‘void InitalizeLibraryServices()’:
./Discover/dllmain.cpp:175: warning: unused variable ‘pi’
./EdgeIO/dllMain.cpp: In function ‘void InitalizeLibraryServices()’:
./EdgeIO/dllMain.cpp:158: warning: unused variable ‘pi’
linking RemKonTester
gflags = -g3
tstlinkflags = -ldl -lpthread
defines = -D__linux__ -D_DEBUG -D_IPC_ARCH_INTEL=1 开发者_如何学C -D_THREAD_SAFE
./RemKonTester/Debug/RemKonTester.o: In function `main':
/home/wmiller/Projects/Eclipse/./RemKonTester/RemKonTester.cpp:81: undefined
reference to `RemKon_EdgeIO::EdgeIoObject::hello()'
/home/wmiller/Projects/Eclipse/./RemKonTester/RemKonTester.cpp:111: undefined
reference to `RemKon_Discover::DiscoverObject::hello()'
/home/wmiller/Projects/Eclipse/./RemKonTester/RemKonTester.cpp:116: undefined
reference to `RemKon_Discover::DiscoverObject::SetLogLevel(unsigned int)'
/home/wmiller/Projects/Eclipse/./RemKonTester/RemKonTester.cpp:117: undefined
reference to `RemKon_Discover::DiscoverObject::hello()'
/home/wmiller/Projects/Eclipse/./RemKonTester/RemKonTester.cpp:118: undefined
reference to `RemKon_Discover::DiscoverObject::LocalIpAddress(int)'
/home/wmiller/Projects/Eclipse/./RemKonTester/RemKonTester.cpp:122: undefined
reference to `RemKon_Discover::DiscoverObject::RegisterCallback(bool(*)
(void*), void*)'
/home/wmiller/Projects/Eclipse/./RemKonTester/RemKonTester.cpp:123: undefined
reference to `RemKon_Discover::DiscoverObject::Search()'
collect2: ld returned 1 exit status
I get the same set of error messages from Eclipse.
RemKonTester.cpp includes all the .h's where these items are declared. I have tried them with the declarations extern "C" and not.
Hoping for help,
Wes
Your problem seems to be the position of -l<library>
:
$(CC) $(gflags) $(tstlinkflags) $(defines) -L ./Debug -ldiscover
-ledgeio -o ./Debug/RemKonTester ./RemKonTester/Debug/RemKonTester.o
./RemKonTester/Debug/logger.o ./RemKonTester/Debug/libraryClass.o
2>&1 | tee ./RemKonTester/make.ERR
They should be after the object files, because the linker loads them when they're met at the command line and search for the undefined symbols.
See man ld
(specifically -l
option) for more info:
-l namespec
...
The linker will search an archive only once, at the location where it is specified on the command line. If the archive defines a symbol which was undefined in some object which appeared before the archive on the command line, the linker will include the appropriate file(s) from the archive. However, an undefined symbol in an object appearing later on the command line will not cause the linker to search the archive again.
This should work for you:
$(CC) $(gflags) $(tstlinkflags) $(defines) -L ./Debug -o ./Debug/RemKonTester ./RemKonTester/Debug/RemKonTester.o ./RemKonTester/Debug/logger.o ./RemKonTester/Debug/libraryClass.o -ldiscover -ledgeio 2>&1 | tee ./RemKonTester/make.ERR
P.S. Note that there an option for editing your question in StackOverflow, posting additional info as an answer is not a good practice.
EDITED: Finally figured out the "editor". Made it prettier. wjm
Sorry for the delay. I had to get a fallen tree out of my front yard last night.
Note: I changes a few names in the original note to make them shorter. This answer has everything spelled out.
g++ output...
$ make
./EdgeIO/dllMain.cpp: In function ‘void InitalizeLibraryServices()’:
./EdgeIO/dllMain.cpp:158: warning: unused variable ‘pi’
./Discover/dllmain.cpp: In function ‘void InitalizeLibraryServices()’:
./Discover/dllmain.cpp:174: warning: unused variable ‘pi’
./RemKonTester/RemKonTester.cpp: In function ‘int main(int, char**)’:
./RemKonTester/RemKonTester.cpp:130: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
./RemKonTester/RemKonTester.cpp:152: warning: unused variable ‘searchResp’
./RemKonTester/libraryClass.cpp: In member function ‘int
library::AttachLibrary()’:
./RemKonTester/libraryClass.cpp:132: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
./RemKonTester/libraryClass.cpp:154: warning: ISO C++ forbids casting between
pointer-to-function and pointer-to-object
./RemKonTester/Debug/RemKonTester.o: In function `main':
/home/wmiller/Projects/Eclipse/
./RemKonTester/RemKonTester.cpp:142: undefined reference to
`RemKon_Discover::DiscoverObject::hello()'
collect2: ld returned 1 exit status
gflags = -g3
tstlinkflags = -ldl -lpthread
defines = -D__linux__ -D_DEBUG -D_IPC_ARCH_INTEL=1 -D_THREAD_SAFE
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemKonDeviceList::~RemKonDeviceList()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::ClientPort(unsigned short)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::PayloadSize(unsigned int)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::Socket() const'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemKonDeviceList::Count() const'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemException::~RemException()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::UDPbinder()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::ListenTimeOut(int)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::ServerPort(unsigned short)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemKonDeviceList::Next()'
./Debug/libdiscover.so: undefined reference to `RemKon_Discover::Log(char
const*, ...)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::UDPlisten(unsigned char*, int*, sockaddr_in*, int*)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemException::RemException()'
./Debug/libdiscover.so: undefined reference to `RemKon_Discover::VerboseLogging()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::RegisterCallbackFunction(void (*)(void*, unsigned
char*, int), void*)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemKonDeviceList::First()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::TestData(unsigned int, unsigned int, unsigned char*,
unsigned int)'
./Debug/libdiscover.so: undefined reference to `RemKon_Discover::Logging()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::MessageLength(int)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::LocalIpAddress(int)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::UDPsend()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::DiscoverObject::LocalIpAddress() const'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::SetBroadcastMode()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::MakeMacAddressString(unsigned char*, char*)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemKonDeviceList::RemKonDeviceList()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemKonDeviceList::New()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::ActivateLogging(unsigned int)'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::RemSocket()'
./Debug/libdiscover.so: undefined reference to
`RemKon_Discover::RemSocket::Payload(unsigned int)'
collect2: ld returned 1 exit status
And this is the makefile. Sorry, I don't speak make that well, so the makefile is functional, not pretty.
CC = @g++
gflags = -g3
cflags = -fPIC -Wall -pedantic
liblinkflags := -shared -fPIC -lstdc++ -lrt -lpthread -rdynamic
tstlinkflags := -ldl -lpthread
defines = -D__linux__ -D_DEBUG -D_IPC_ARCH_INTEL=1 -D_THREAD_SAFE
all : clean edgeio discover tester
############################################
edgeio : ./EdgeIO/dllMain.o ./EdgeIO/EdgeIO.o
$(CC) $(gflags) $(liblinkflags) $(defines) -Wl,-soname,./Debug
/libedgeio.so.1 -o ./Debug/libedgeio.so.1.0 ./EdgeIO/Debug/edgeio.o
./EdgeIO/Debug/dllmain.o 2>&1 | tee ./EdgeIO/make.ERR
@cd ./Debug; ln -sf libedgeio.so.1.0 libedgeio.so
@cd ./Debug; ln -sf libedgeio.so.1.0 libedgeio.so.1
./EdgeIO/dllMain.o : ./EdgeIO/dllMain.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o./EdgeIO/Debug/dllmain.o ./EdgeIO/dllMain.cpp 2>&1
| tee ./EdgeIO/dllmain.ERR
./EdgeIO/EdgeIO.o : ./EdgeIO/EdgeIO.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o./EdgeIO/Debug/edgeio.o ./EdgeIO/EdgeIO.cpp 2>&1 |
tee ./EdgeIO/EdgeIO.ERR
############################################
discover : ./Discover/Discover.o ./Discover/dllmain.o
$(CC) $(gflags) $(liblinkflags) $(defines) -Wl,-soname,./Debug
/libdiscover.so.1 -o ./Debug/libdiscover.so.1.0 ./Discover/Debug
/Discover.o ./Discover/Debug/dllmain.o 2>&1 | tee ./Discover/make.ERR
@cd ./Debug; ln -sf libdiscover.so.1.0 libdiscover.so
@cd ./Debug; ln -sf libdiscover.so.1.0 libdiscover.so.1
./Discover/Discover.o : ./Discover/Discover.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o./Discover/Debug/Discover.o ./Discover/Discover.cpp
2>&1 | tee ./Discover/Discover.ERR
./Discover/dllmain.o : ./Discover/dllmain.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o./Discover/Debug/dllmain.o ./Discover/dllmain.cpp
2>&1 | tee ./Discover/dllmain.ERR
############################################
tester : ./RemKonTester/RemKonTester.o ./common/logger.o ./RemKonTester
/libraryClass.o ./common/RemException.o
$(CC) $(gflags) $(tstlinkflags) $(defines)
-L ./Debug -o ./Debug/RemKonTester ./RemKonTester/Debug
/RemKonTester.o ./RemKonTester/Debug/logger.o ./RemKonTester/Debug
/libraryClass.o 2>&1 | tee ./RemKonTester/make.ERR
@echo "gflags = $(gflags)" 2>&1 | tee ./RemKonTester/make.ERR
@echo "tstlinkflags = $(tstlinkflags)" 2>&1 | tee ./RemKonTester/make.ERR
@echo "defines = $(defines)" 2>&1 | tee ./RemKonTester/make.ERR
$(CC) $(gflags) $(tstlinkflags) $(defines) -L ./Debug -ldiscover
-ledgeio -o ./Debug/RemKonTester ./RemKonTester/Debug/RemKonTester.o
./RemKonTester/Debug/logger.o ./RemKonTester/Debug/libraryClass.o
2>&1 | tee ./RemKonTester/make.ERR
./RemKonTester/RemKonTester.o : ./RemKonTester/RemKonTester.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o ./RemKonTester/Debug/RemKonTester.o ./RemKonTester
/RemKonTester.cpp 2>&1 | tee ./RemKonTester/RemKonTester.ERR
./common/logger.o : ./common/logger.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o ./RemKonTester/Debug/logger.o ./common/logger.cpp
2>&1 | tee ./RemKonTester/logger.ERR
./RemKonTester/libraryClass.o : ./RemKonTester/libraryClass.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o ./RemKonTester/Debug/libraryClass.o
./RemKonTester/libraryClass.cpp 2>&1 | tee ./RemKonTester
/libraryClass.ERR
./common/RemException.o : ./common/RemException.cpp
$(CC) $(gflags) $(cflags) -c $(defines) -I ./common -I ./EdgeIO
-I ./Discover -o ./RemKonTester/Debug/RemException.o ./common
/RemException.cpp 2>&1 | tee ./RemKonTester/RemException.ERR
############################################
clean :
@rm -f *.ERR
@rm -f ./common/*.ERR
@rm -f ./EdgeIO/*.ERR
@rm -f ./Discover/*.ERR
@rm -f ./RemKonTester/*.ERR
@rm -f QEMACRO*
@rm -f ./common/EdgeIO/QEMACRO*
@rm -f ./EdgeIO/QEMACRO*
@rm -f ./Discover/QEMACRO*
@rm -f ./RemKonTester/QEMACRO*
@rm -f ./Debug/*.o
@rm -f ./common/Debug/*.o
@rm -f ./EdgeIO/Debug/*.o
@rm -f ./Discover/Debug/*.o
@rm -f ./RemKonTester/Debug/*.o
@rm -f ./Debug/*.log
@rm -f ./common/Debug/*.log
@rm -f ./EdgeIO/Debug/*.log
@rm -f ./Discover/Debug/*.log
@rm -f ./RemKonTester/Debug/*.log
@rm -f ./Debug/*.so*
############################################
Thanks,
Wes
精彩评论