views:

189

answers:

2

Hello,

I have an issue. I have defined in C file, read-line.c, a function print, like this:

void history_print(void)
{
    /* some stuff */
}

In a C++ file, command.cc, I have the following:

extern "C" void history_print(void);

and then I simply call history_print().

#Use GNU compiler
cc = gcc -g
CC = g++ -g   

all: shell

tty-raw-mode.o: tty-raw-mode.c
    gcc -c tty-raw-mode.c

read-line.o: read-line.c
    gcc -c read-line.c

lex.yy.o: shell.l
    lex shell.l
    $(cc) -c lex.yy.c

y.tab.o: shell.y
    yacc -d shell.y
    $(CC) -c y.tab.c

command.o: command.cc
    $(CC) -c command.cc

shell: y.tab.o lex.yy.o tty-raw-mode.o read-line.o command.o
    $(CC) -o shell lex.yy.o y.tab.o tty-raw-mode.o read-line.o command.o -ll -lgen

I have an issue when it comes to linking, in my Makefile, for the rule output:

Undefined                       first referenced
 symbol                             in file
history_print                       command.o
ld: fatal: Symbol referencing errors. No output written to shell
collect2: ld returned 1 exit status
*** Error code 1
make: Fatal error: Command failed for target `shell'

Output of -v option flag for make:

yacc -d shell.y
g++ -g -v -c y.tab.c
Reading specs from /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/specs
Configured with: ../sources/gcc-3.4.6/configure --prefix=/opt/csw/gcc3 --with-local-    prefix=/opt/csw --without-gnu-as --with-as=/usr/ccs/bin/as --without-gnu-ld     --with-ld=/usr/ccs/bin/ld --enable-threads=posix --enable-shared --enable-multilib     --enable-nls --with-included-gettext --with-libiconv-prefix=/opt/csw --with-x --enable-    java-awt=xlib --enable-languages=all
Thread model: posix
gcc version 3.4.6
 /opt/csw/gcc3/libexec/gcc/sparc-sun-solaris2.8/3.4.6/cc1plus -quiet -v y.tab.c -quiet     -dumpbase y.tab.c -mcpu=v7 -auxbase y.tab -g -version -o /var/tmp//ccCi8vXj.s
ignoring nonexistent directory "/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../..    /../../sparc-sun-solaris2.8/include"
#include "..." search starts here:
#include <...> search starts here:
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/sparc-    sun-solaris2.8
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/backward
 /opt/csw/include
 /opt/csw/gcc3/include
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/include
 /usr/include
End of search list.
GNU C++ version 3.4.6 (sparc-sun-solaris2.8)
        compiled by GNU C version 3.4.6.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 /usr/ccs/bin/as -V -Qy -s -xarch=v8 -o y.tab.o /var/tmp//ccCi8vXj.s
/usr/ccs/bin/as: SunOS 5.10 118683-05 Patch 04/30/2010
lex shell.l
gcc -g -c lex.yy.c
gcc -c tty-raw-mode.c
gcc -c read-line.c
g++ -g -v -c command.cc
Reading specs from /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/specs
Configured with: ../sources/gcc-3.4.6/configure --prefix=/opt/csw/gcc3 --with-local-    prefix=/opt/csw --without-gnu-as --with-as=/usr/ccs/bin/as --without-gnu-ld --with-ld=/usr/ccs/bin/ld --enable-threads=posix --enable-shared --enable-multilib --enable-nls --with-included-gettext --with-libiconv-prefix=/opt/csw --with-x --enable-    java-awt=xlib --enable-languages=all
Thread model: posix
gcc version 3.4.6
 /opt/csw/gcc3/libexec/gcc/sparc-sun-solaris2.8/3.4.6/cc1plus -quiet -v command.cc -quiet -dumpbase command.cc -mcpu=v7 -auxbase command -g -version -o /var/tmp//cckVWlC7.s
ignoring nonexistent directory "/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../sparc-sun-solaris2.8/include"
#include "..." search starts here:
#include <...> search starts here:
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/sparc-sun-solaris2.8
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../../../include/c++/3.4.6/backward
 /opt/csw/include
 /opt/csw/gcc3/include
 /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/include
 /usr/include
End of search list.
GNU C++ version 3.4.6 (sparc-sun-solaris2.8)
        compiled by GNU C version 3.4.6.
GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072
 /usr/ccs/bin/as -V -Qy -s -xarch=v8 -o command.o /var/tmp//cckVWlC7.s
/usr/ccs/bin/as: SunOS 5.10 118683-05 Patch 04/30/2010
g++ -g -v -o shell lex.yy.o y.tab.o tty-raw-mode.o read-line.o command.o -ll -lgen
Reading specs from /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/specs
Configured with: ../sources/gcc-3.4.6/configure --prefix=/opt/csw/gcc3 --with-local-    prefix=/opt/csw --without-gnu-as --with-as=/usr/ccs/bin/as --without-gnu-ld --with-ld=/usr/ccs/bin/ld --enable-threads=posix --enable-shared --enable-multilib --enable-nls --with-included-gettext --with-libiconv-prefix=/opt/csw --with-x --enable-    java-awt=xlib --enable-languages=all
Thread model: posix
gcc version 3.4.6
 /opt/csw/gcc3/libexec/gcc/sparc-sun-solaris2.8/3.4.6/collect2 -V -R /opt/csw/lib -Y P,/opt/csw/lib:/usr/ccs/lib:/usr/lib -Qy -o shell /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crt1.o /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crti.o     /usr/ccs/lib/values-Xa.o /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crtbegin.o -L/opt    /csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6 -L/usr/ccs/bin -L/usr/ccs/lib -L/opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/../../.. lex.yy.o y.tab.o tty-raw-mode.o read-line.o command.o -ll -lgen -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc -lc /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crtend.o /opt/csw/gcc3/lib/gcc/sparc-sun-solaris2.8/3.4.6/crtn.o
ld: Software Generation Utilities - Solaris Link Editors: 5.10-1.497
Undefined                       first referenced
 symbol                             in file
history_print                       command.o
ld: fatal: Symbol referencing errors. No output written to shell
collect2: ld returned 1 exit status
*** Error code 1
make: Fatal error: Command failed for target `shell'

I guess the issue is due to linking, and it has to do with the Makefile, but I am not sure how to fix. Could anyone please help me?

Thank you very much, I really appreciate your help.

Jary

+5  A: 

It's because you're recompiling the a.c file with g++ and that doesn't have an extern C wrapper around the function definition. Hence the name will undergo C++ mangling and there will be no print in the a.o object file.

However, b.cc will think that print is a C name (since it has the extern C) so it will go looking for the unmangled version.

Either put extern C around the definition in a.c (which probably makes that unusable with a regular C compiler, so keep that in mind) or change your g++ command to use a.o (which has the unmangled name by virtue of the fact it was done with the C compiler) instead of a.c.


In fact, that won't work because you're not invoking the linker in the g++ command. I'm not even sure why you're trying to include a.o into b.o. Probably the best time to do that is at link time to create an ab executable, not compile time, something like:

CC = g++ -g

a.o: a.c
    gcc -o a.o -c a.c

b.o: b.cc
    $(CC) -o b.o -c b.cc

ab: a.o b.o
    $(CC) -o ab a.o b.o

Update: Breaking it down to the simplest test case so you can see what I mean:

a.c:
    #include <stdio.h>
    void print(void) {
        printf ("7\n");
    }

b.cc:
    extern "C" void print(void);
    int main(void) {
        print();
        return 0;
    }

Makefile:
    output: a.o b.o Makefile
        g++ -o output a.o b.o

    a.o: a.c Makefile
        gcc -o a.o -c a.c

    b.o: b.cc Makefile
        g++ -o b.o -c b.cc

Then running make and the program:

pax> make
gcc -o a.o -c a.c
g++ -o b.o -c b.cc
g++ -o output a.o b.o

pax> ./output
7

If you change the compilation of a.c to use g++, that gives you your original error where it cannot find print:

pax> make
g++ -o a.o -c a.c
g++ -o b.o -c b.cc
g++ -o output a.o b.o
b.o:b.cc:(.text+0x2b): undefined reference to `_print'
collect2: ld returned 1 exit status
make: *** [output] Error 1

Now it appears that your final Makefile is correct. It compiles a.c with the C compiler so that the name mangling does not take place. My advice is to clean everything (remove all the *.o files) and run make again, then post the output here at the bottom of your question.

It may be that some of the files are older ones which is why you should delete all the object files before running make.

paxdiablo
Replacing a.c by a.o, I get the same error, but with a warning before: g++: a.o: linker input file unused because linking not done. How can I please fix this?
Jary
Using a.o doesn't help because that is a compilation-to-object command, not a linking command.
Jonathan Leffler
@Jary, see the update.
paxdiablo
When using -o rather than -c, I get the following: g++: no input files*** Error code 1make: Fatal error: Command failed for target `a.o'
Jary
I posted a more complete code, I hope you can help me. Sorry I'm having such issues with Makefile.
Jary
@Jary, you don't use `-o` _instead_ of `-c`, you use it _in addition to_. I put that in since I like to always explicitly set the output file since many linkers invariably create an `a.out` file instead of something more intelligently named. And I can't see anything immediately wrong with your latest update. What problem are you having now?
paxdiablo
Thank you. I have added the extra -o as you mentioned and it worked but the error is still here. The undefined symbol error still shows up.
Jary
most probably your `make` command knows about which is are the C and C++ compilers and about naming conventions for C and C++ filenames. It will just do the right thing if you don't give *rules*. Just giving the dependencies themselves should suffice. Then remove all .o files, and try again.
Jens Gustedt
I tried your solution but that does not work. It tries to use g++ for .c files and complains about unknown functions.
Jary
@Jary, see the further update. Please clean up your object files then run `make` and post the output here (at the bottom of your question).
paxdiablo
+1  A: 

Your makefile needs a linking rule rather than the separate compilation rules:

CC  = gcc -g
CPP = g++ -g

program: a.o b.o
    ${CPP} -o $@ a.o b.o

Make already knows how to convert source into object files - you don't need to retrain it.

Jonathan Leffler
Well actually, I have more linking rules, I just displayed the ones that were producing an issue at this point.
Jary
OK - then don't compile a.c with the C++ compiler; it is, after all, C source, not C++ source. And, despite the plethora of C/C++ questions on SO, they are separate (though related) languages. This question actually **would be** correctly tagged with C and C++ since it involves both languages, albeit in separate source files.
Jonathan Leffler
I think I actually have what you propose Jonathan. Please check the bottom of my initial post, I posted the whole makefile. In output, it seems that what you are telling me is what I am doing, isn't it?
Jary
@Jary: what you've got in the whole makefile is different from what you originally posted - which still appears at the top. The original `b.o` rule should not be invoking the loader - I'm not wholly convinced that you posted the correct makefile fragment. Your lex and yacc rules are a little simplistic too - lending further credence to 'what we see is not quite what you execute'. Your whole makefile looks OK.
Jonathan Leffler
@Jary: Please note as D McKee points out that CC is reserved by POSIX make for the C compiler; it should not be used for the C++ compiler. Either CXX or CPP is recommended for the C++ compiler.
Jonathan Leffler
Sorry when I first posted the makefile, I wanted to post only what I thought was necessary. What I posted bellow is what I have, and I get the symbol error when it tries to execute output, giving me the undefined symbol print in b.o. I am sorry, it's not actually the b.o rule that it stops at, it's at the output rule, that is my mistake, I see I simplified too much and removed the real problem.
Jary
I've updated my post, hopefully the issue becomes more clear now. The error is the same but it actually happen for output. The Makefile that I now have in my initial post was identical before I added the extern function call, it only started complaining after that call was added.
Jary
@Jary: remove a.o and rebuild. I think it got compiled once with the C++ compiler and therefore defines the C++ link-safe name for `print()` instead of the undecorated C name. Alternatively, on Linux or Unix machines, use `nm -g a.o` to see what function is defined in `a.o`; my guess is that the name is not `print`!
Jonathan Leffler
To avoid confusion, I just renamed print to printTest. I removed all the .o files, and recompiled. I still get the error. After nm - g a.o I get: [23] | 64| 228|FUNC |GLOB |0 |5 |printTest; but I still have the undefined error, the symbol is now printTest though. Thanks.
Jary
@Jary: it looks like you're using Solaris? Very weird...the `nm` says the symbol is there, but the rest of the link says it is not. Clutching at straws: there's no other error message - nothing about wrong architecture or anything? More plausibly: what does 'nm -g b.o | grep printTest' give you - a decorated or an undecorated name? This should be an UNDEF entry - the reference to `printTest()`.
Jonathan Leffler
Yes I am using Solaris. Nothing about wrong architecture, just undefined symbol, ld: fatal: Symbol referencing errors., collect2: ld returned 1 exit status*** Error code 1make: Fatal error: Command failed for target. For the nm -g b.o, I get [25] | 0| 0|NOTY |GLOB |0 |UNDEF |printTest; apparently it's UNDEF.
Jary
@Jary: mysteriouser and mysteriouser...are the file names really single letters, or are you mapping them for the purposes of the question? It's behaving as if `a.o` is not handled by the linker, or as if it is pulling the file from somewhere other than where you expect. Since you're using G++/GCC, can you trace the commands executed with the '-v' option: `make CPP='g++ -g -v'`. The output will be verbose - probably won't fit in a comment, so amend the question.
Jonathan Leffler
I do not change the order of the files, but I did rename them, there are more than one character each, there's just a bit cumbersome. I am posting the output in the original command.
Jary
Thanks Jonathan. I have actually completely updated the Makefile to reflect what I actually have. It will make reading the output easier for you I think. Now you have exactly what I get, so no renaming of files or function calls.
Jary
@Jary: thanks. I suppose history_print() is defined in read-line.c? I don't see what is going on yet (it all looks kosher) - and it is 5 past bedtime for me. I'll take another look in the morning. I do have a Solaris 10 SPARC with GCC 3.4.6 still on it. If you wanted to send me the code and makefile in a gzipped tar file (see my profile for email address), I might be able to reproduce the issue, or resolve it. But I won't be looking for 7 hours or so now.
Jonathan Leffler
history_print is defined in read-line.c. I do not mind sending you the code. Thanks a lot, I really appreciate it.
Jary
@Jary, I think your linker may be somehow broken. `nm` on both files agree that the function is called `printTest`, defined in a.o with a reference in b.o yet the linker refuses to find it. Try to test with GNU's own linker if available and/or put command.o at the start of the list (that shouldn't matter but, once we get into suspect tools mode, we need to check everything).
paxdiablo
@Jary: with the code on hand, I can see a 'late night thinko' - you call `history_print()` but you define `print_history()`.
Jonathan Leffler
@paxdiablo: the name mapping used in the early revisions of the question hid the problem.
Jonathan Leffler
Thank you so much Jonathan! It was a really stupid error: I did not type the function name correctly. I apologize for that!
Jary