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

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!

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.

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.

Related

Having difficulties compiling simple c++ program on the command line with multiple files; maybe a linker error?

Sorry for the simple question. I am attempting to learn more c++ at a fundamental level. I have always used VS in the past, and I am trying to learn the command line and compile, navigate, etc. with it.
I started with "hello world" and was able to compile it with gcc/clang, then run it with the expected results.
I then slightly reworked this and made a new header/cpp file to do the output part of hello world, and then call that from the main function, described below:
main.cpp:
#include "MyClass.h"
int main(){
foo();
return 0;
}
MyClass.h
#pragma once
void foo();
MyClass.cpp
#include "MyClass.h"
#include <iostream>
void foo(){
std::cout << "Hello World\n";
}
I then have tried to compile with gcc and clang as follows:
clang -Wall -g main.cpp MyClass.cpp
I have tried the same with GCC, and have also tried various invocations of this, such as using -c:
clang -Wall -g -c main.cpp
clang -Wall -g -c MyClass.cpp
Each and every time, I get an error
λ clang -Wall -g MyClass.cpp main.cpp
main.cpp:13:1: error: use of undeclared identifier 'foo'
foo();
^
1 error generated.
I get this same error whether using gcc or clang.
I also tried from scratch on my laptop, to see if there was some more global issue, but I still get the same problem.
I have also tried on the basic Windows command line as well.
Other areas on StackOverflow demonstrate simple ways of compiling multiple files from the command line, and I have tried as they show, but still get errors.
I also know that "make" is something I need to learn as well, however, I just want to make sure I understand what my make file is doing before I dive into that.
I feel like it must be something trivial that I just cannot figure out.
Thank you to Andreas for the suggestion of looking at the preprocessor output. And thank you to everyone for the suggestions.
The pre-processor output did not make sense to what I was compiling.
I was using VSCode, in this case, as a text editor, making brand new files in my folder after launching it from the command line. I thought the files I created in VSCode directly into the folder (named main.cpp, for example), would produce a regular text file. However, for some reason, it did not.
Essentially, I recreated the above program in notepad and was easily able to compile it using the commands I used above. I guess VSCode may not be perfect for me as a pure text editor or I should figure out if there are settings to change to accomplish my goal.
Thank you all again for your time and consideration.
Use extern on your function. Also make sure you're compiling with c++ and not c; i.e. g++.
MyClass.h
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
extern void foo();
#ifdef __cplusplus
}
#endif

Multiple includes of same header file in project: C vs C++

Here I have an example project with two source files and a header file, as follows:
main.c:
#include<stdio.h>
#include "personal.h"
int main(){
i = 5;
printf("Value is %d\n",i);
return 0;
}
sub.c:
#include "personal.h"
// do nothing
and finally personal.h:
#pragma once
int i;
Each of the .c file includes the personal.h, which is `guarded'. I compile with gcc, all goes fine:
>gcc sub.c main.c -o out
>./out
Value is 5
But with g++, this happens:
>g++ sub.c main.c -o out
/tmp/cctYwVnO.o:(.bss+0x0): multiple definition of `i'
/tmp/ccPElZ27.o:(.bss+0x0): first defined here
collect2: error: ld returned 1 exit status
Is there anything fundamentally different between C++ and C in terms of how files are linked, preprocessor activity etc? I tried the same with other compilers like clang and the same happens. I am perhaps missing something silly here.
In C,
int i;
is a tentative definition. By the virtue of inclusion, you have a tentative definition for i in two compilation units. The C standard allows to have tentative definitions in multiple compilation units, but does not mandate that implementation accept that. The custom behavior for Unix C compilers is to allow it but gcc has an option (-fno-common) to prevent it and generate an error at link time (so that you can check the code for compilers, as I think Microsoft one, which does not allow it or for platforms for which that allow better code -- I know of none but that's a rationale given by GCC documentation).
IIRC, C++ has no such allowance.
Note that you probably want a declaration and not a definition in headers. Thus for the i above it should be
extern int i;
in the header and one
int i;
at global scope in one .c file.
sub.c will include personal.h and will create the variable i in global scope. Similarly, main.c will also include personal.h and create variable i in global scope. Eventually, when you link, there are two definitions of i in the global scope and hence the error.

Clang and C++ Module TS

I have difficulty getting modules ts work on clang (both supplied with Xcode 9.3 beta and latest 7.0.0 trunk). I checked out other answers both here and on other platforms but I still can't figure it out. Here is what I have so far:
main.cpp
#include <iostream>
import stuff;
int main() {
std::cout << whatever() << "\n";
}
module.cppm
export module stuff;
export int whatever() {
return 5;
}
Following various recommendations, I have done this:
clang -fmodules-ts -std=c++17 --precompile -o stuff.pcm module.cppm
clang -fmodules-ts -std=c++17 -fmodule-file=stuff.pcm main.cpp
This results in linker error (whatever() not found). I am not sure how to proceed here.
Notes: I tried -fprebuilt-module-path=. instead of -fmodule-file and then it complains about unknown modules.
If someone can tell me how to set it up within Xcode, even better.
Disclaimer: I am not interested in a solution using module maps and traditional include. My interest in modules is not about improving compile times but about properly modularising my code (especially template code). Right now all my code is in header files anyway with the majority of functions declared as static.
It turns out that one needs to compile the precompiled module file as well, either by compiling it to an object file and then linking that file, or via a shortcut:
clang++ -fmodules-ts -std=c++17 -o main -fmodule-file=module.pcm main.cpp module.pcm
I guess this makes some sense (if one sees modules as AST dumps, and hence just another kind of source file). I have also found out that one can use -fprebuilt-module-path=. option instead of specifying -fmodule-file if the name of the module and the name of the module file is the same.
That said, there are a lot of things that still confuse me. For instance, attributes such as always_inline or noinline don't seem to be honoured. The compiler will inline functions though when optimisation is on.

Error launching C++ executable in AIX

I am facing a unique problem as part of our application migration from HP to AIX.
The following simulated code produces different results in HP and AIX.
library.C **
#include <stdio.h>
#include "mylib.h"
int libimgclientFNXXX()
{
int check = 100;
check = FileNetDeleteDoc(check);
return check;
}
int libimgclientFN()
{
int check = 1;
printf("In lib ");
return check;
}
* main_func.C *
#include <stdio.h>
int libimgclientFN();
int libimgclientFNXXX();
int main()
{
int one = 0;
if (1 == 1)
{
one = libimgclientFN();
}
printf("\n The status is %d \n", one);
}
* mylib.h **
extern int FileNetDeleteDoc (int);
Note that the function libimgclientFNXXX() is never called.
My make file is as below:
xlC -c -g library.C -o library.o -I./
xlC -G -qmkshrobj -o libImgClient.so library.o
xlC -c -g -qpic=small main_func.C -o main_func.o
xlC -brtl main_func.o -L. -lImgClient -o TST
When I run TST, I get the following loading error
$ TST
exec(): 0509-036 Cannot load program TST because of the following errors:
rtld: 0712-001 Symbol FileNetDeleteDoc__Fi was referenced
from module ./libImgClient.so(), but a runtime definition
of the symbol was not found.
Even though the function libimgclientFNXXX() is never called, there is unresolved errors.
The exact same code built in HP works fine with no errors.
Any inputs is appreciated.
Thanks,
Yeah, "not using" a library function may still be an error, even if you are not going to call the code. It MAY defer the loading of some componanent until later, so it MAY not cause an error. Best not to have references to things that don't exist (or manually load the library and get the address, if the function doesn't exist, you'll get an error from the "find the function" call and you can do something sensible in the code).
The loader (the code that loads binary executables) isn't very clever, so it can't know exactly what is being called and what isn't. It's also possible that different compilers have different levels of cleverness for "dead code removal", so the one compiler completely removes the "never called" function, but another compiler doesn't remove it [because it's not got the cleverness to 100% certify that you never call the function - in gcc for example, it would know this if you made the libimgclientFNXXX a static function - because it knows that static functions don't get called outside this module, and this module isn't using it.
AIX requires all symbols to resolve at load-time, so even though it builds OK, because the symbol is referenced the applications will not run.
You need to use lazy linking for the .so (the -blazy link option), which should cause the missing function to be linked only on first use.
You really should not be leaving undefined symbols in a library, though - if it needs symbols from another library, you should be linking to them (unless it's a plug-in, where the symbol is exposed in the app itself).

C++: Switching from MSVC to G++: Global Variables

I recently switched to Linux and wanted to compile my Visual Studio 2010 C++ source code, which uses only the STL, on G++.
My Linux machine currently isn't available but I can try to tell you what is going on, first:
As I try to compile my project, all global variables I use in main and which perfectly work on MSVC result in myGlobalVar is not defined in this scope errors.
My project is built nearly the same as the example below:
// myclass.h
class myClass
{
// ....
};
extern myClass globalInstance;
// myclass.cpp
#include "myclass.h"
// myClass functions located here
myClass globalInstance;
// main.cpp
#include "myclass.h"
int main( )
{
// Accessing globalInstance results in an error: Not defined in this scope
}
What am I doing wrong?
Where are the differences between G++ and MSVC in terms of global variables?
you need to compile as follow:
g++ main.cpp myclass.cpp -o myapp
NOT as follow:
g++ main.cpp -o myapp which will miss global variable declaration in myclass.cpp file.
Your sample code should work just fine on Linux as well as Windows. There shouldn't be any differences between GCC & MSVC with regards to visibility of global variables. I think it's more likely that what you're seeing is a symptom of another problem.
The only thing I can think off off the top of my head that might cause an issue like this would be "screwed up" header files, to use the technical term for it. A common issue in porting code from Windows to Linux is header file case sensitivity. Whereas MSVC won't care if you import MyHeader.h as #include <myheader.h> it will certainly fail on Linux. If you header isn't being included, the compiler would miss the extern declaration and might result in the error you're seeing.