1

I have a really perplexing problem in GCC.

I get the following error:

gcc -Wall -Werror -L/Users/red_angel/chorebox_sys/lib -o products/chbc2c -lchorebox ofiles/main.o
Undefined symbols for architecture x86_64:
  "_chbclib_flushout", referenced from:
      _main in main.o
  "_chorebox_argc", referenced from:
      _chorebox_command_line in libchorebox.a(chorebox_command_line.o)
  "_chorebox_argv", referenced from:
      _chorebox_command_line in libchorebox.a(chorebox_command_line.o)
  "_chorebox_env", referenced from:
      _chorebox_command_line in libchorebox.a(chorebox_command_line.o)
  "_mn_command_options", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [products/chbc2c] Error 1

What's wrong with this error? I have confirmed that the _chorebox_argc symbol is indeed present in "libchorebox.a".

I confirmed it by running the command:

nm /Users/red_angel/chorebox_sys/lib/libchorebox.a | cat -n | chodo -_chorebox_argc flip

As the "chodo" command is an command I wrote that you may not be familiar - I will explain what it does. It reads from Standard Input, and forward to Standard Output every line that matches the search pattern. In this case (to make a long story short) it outputs every line containing the "_chorebox_argc" string.

I get the following output:

     3  0000000000000004 C _chorebox_argc
    55                   U _chorebox_argc

To get a closer look at the relevant part of the file, I type the same command, only this time omitting the "chodo" command at the end of the piped series of commands --- and hereby will copy/paste to you the relevant part of that file:

     1  
     2  /Users/red_angel/chorebox_sys/lib/libchorebox.a(vars.o):
     3  0000000000000004 C _chorebox_argc
     4  0000000000000008 C _chorebox_argv
     5  0000000000000008 C _chorebox_env
     6  
     7  /Users/red_angel/chorebox_sys/lib/libchorebox.a(chorebox_mlc.o):
     8  00000000000000c8 s EH_frame0
     9  0000000000000075 s L_.str
    10                   U ___stderrp
    11                   U _chorebox_argv
    12  0000000000000000 T _chorebox_mlc
    13  00000000000000e0 S _chorebox_mlc.eh
    14                   U _exit
    15                   U _fflush
    16                   U _fprintf
    17                   U _malloc
    18  
    19  /Users/red_angel/chorebox_sys/lib/libchorebox.a(chorebox_apend_string.o):
    20  0000000000000078 s EH_frame0
    21  0000000000000000 T _chorebox_apend_string
    22  0000000000000090 S _chorebox_apend_string.eh
    23                   U _chorebox_join_string
    24                   U _free
    25  

Needless to say ---- the symbol is definitely present in the "libchorebox.a" file ----- so why is the GCC linker complaining that it is not found?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Sophia_ES
  • 1,193
  • 3
  • 12
  • 22
  • `ld: symbol(s) not found for architecture x86_64` `clang: error: linker command failed with exit code 1 (use -v to see invocation)`: can you enforce that $LD points to GNU ld, so that we might not get into compatibility trouble with clang? – Marcus Müller Feb 11 '15 at 21:30
  • Well ---- the library was built on the same computer with the same version of GCC --- so the existence of incompatibilities is in itself perplexing. Besides that, I'm not sure I understand what you're saying. – Sophia_ES Feb 11 '15 at 21:32
  • @MarcusMüller ---- how do I enforce that $LD points to GNU ld? Do I take it /usr/bin/ld isn't the GNU ld? And if so --- how do I find out where the GNU ld is? – Sophia_ES Feb 11 '15 at 21:41
  • Okay @MarcusMüller --- I attempted your suggestion (as best as I could understand) -- and it still was to no avail: Here is the pastebin: http://pastebin.com/2msWRvEp – Sophia_ES Feb 11 '15 at 21:43
  • possible duplicate of [Linker order - GCC](http://stackoverflow.com/questions/45135/linker-order-gcc) – Jonathan Leffler Feb 11 '15 at 23:06
  • @JonathanLeffler ---- every variation of link order has been tried - to no avail. Also, in building the library, I changed the order of the component files --- also to no avail. If you hypothesis were correct, one of these approaches aught to have made a dent in the problem -- but none has. – Sophia_ES Feb 11 '15 at 23:49
  • @JonathanLeffler --- by the way ---- I already asked (back when Marcus first pointed out what seemed to the be the problem) how come GCC is generating stuff that is incompatible with itself. Unless someone can offer an explanation *why* GCC is behaving in such an inconsistent manner on the same account of the same machine with the same version of GCC - and what can be *done* about it - it is a non-answer. – Sophia_ES Feb 11 '15 at 23:52
  • I can tell GCC to create code that is incompatible for linking with other code that the same copy of GCC created. All it requires is the (ab)use of `-m32` and `-m64` in different parts of the compilation process. Are you on a Mac? – Jonathan Leffler Feb 11 '15 at 23:53
  • @JonathanLeffler --- I might add --- both in the project that builds the library *and* in the project that attempts to *use* it (as I wrote all the code in both projects) -- at *no* point are *any* options passed to GCC that should in any way alter the processor or address-size that the compiler would use. At both locations, it should be using the system-default (and that's on the same account of the same machine, same version of GCC) --- so the inconsistency you are suggesting simply does not make sense. – Sophia_ES Feb 11 '15 at 23:55
  • One day, you'll answer my question: did you establish whether the object files in libchorebox.a are 32-bit or 64-bit files? If they are 64-bit, I'm barking up the wrong tree. If they are 32-bit, then I'm on the right track, regardless of what you or I think should be happening. We have to work on evidence, not faith. I agree it 'should not happen'; however, the question is 'is it happening' -- and until we know the answer to that, you are unhelpable. – Jonathan Leffler Feb 11 '15 at 23:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70745/discussion-between-sophia-es-and-jonathan-leffler). – Sophia_ES Feb 12 '15 at 00:02

2 Answers2

3

Put the -l option after the file that needs it (ofiles/main.o)

See this question for more information on link order.

Community
  • 1
  • 1
  • That's the way it originally was ----- and I was getting the same error. To demonstrate this, I'm going to revert it back to it's original version (where the -l option was after the file that needed it) and show it to you in a pastebin. The problem I am experiencing is when one object in that ".a" archive requires a *different* object *within* the ".a" archive. But anyway -- here's the pastebin: http://pastebin.com/uNE4QTZx – Sophia_ES Feb 11 '15 at 22:22
  • @Sophia_ES: the key is probably the information `Undefined symbols for architecture x86_64`. The library is probably a 32-bit library, not a 64-bit library (or, more precisely, the object files in the library are probably 32-bit object files, not 64-bit object files). You can test with: `mkdir junk; cd junk; ar x /Users/red_angel/chorebox_sys/lib/libchorebox.a chorebox_apend_string.o; file chorebox_apend_string.o ../ofiles/main.o; cd ..; rm -fr junk`, noting that the two object files have different types. An educated guess, but technically still a guess. – Jonathan Leffler Feb 11 '15 at 23:10
  • @JonathanLeffler --- how come GCC is generating libraries that are incompatible with itself on the same computer? How do you explain this? As I have explained repeatedly --- every step of compilation and building is done on the *same* machine with the *same* version of GCC. If your explanation is the answer ---- why is GCC behaving in such a crazy way? – Sophia_ES Feb 11 '15 at 23:46
  • @Sophia_ES: It can be told to compile 32-bit or 64-bit; it probably compiles 64-bit by default (at least, your build is looking for 64-bit), but it seems plausible that the chorebox library was built for 32-bit. Since I've no idea where you got the library from, or how you built it, or what else there is in proximity to the library you have (is there a `lib64` directory next door to the `lib` directory, for example?), it is hard to diagnose what you need to be doing. Do I take it that the test I outlined did show 32-bit vs 64-bit code (x86 vs x86_64, or something similar)? – Jonathan Leffler Feb 11 '15 at 23:50
  • This seems unlikely, but maybe you need to ranlib that library –  Feb 12 '15 at 01:09
  • @JonathanLeffler Hi. Do you think the error here http://stackoverflow.com/questions/38802547/googletest-undefined-symbols-for-architecture-x86-64-error could be explained by the same phenomenon ? (I doubt, it is google test after all.) – Olórin Aug 07 '16 at 15:54
  • @user10000100_u: the error in the other question (your question) almost certainly stems from a mixture of 32-bit and 64-bit compilations. In case of doubt, add `-m64` (or, if you're really sure, `-m32`) to the compiler name (so `CC = …/gcc-6.0.0 -m64`, etc) to make sure everything is compiled with the `-m64` option. Rebuild the GoogleTest libraries with that too; make sure `cmake` is using the option too. I've not run into problems recently (but I upgraded from Yosemite 10.10.x to El Capitan 10.11.x a long time ago) — and I build my own GCC (currently 6.1.0) using current XCode to bootstrap. – Jonathan Leffler Aug 07 '16 at 16:10
  • @JonathanLeffler 1st of all thx for your answer here ! With `-m64` I get the following output : https://bitbucket.org/snippets/MisesEnForce/k49ML the same as before. With `-m32` I get the following much more shorter output : https://bitbucket.org/snippets/MisesEnForce/b48op ... I used apple's clang to bootstrap GCC 6.1.0 as well yesterday in the night, thinking the problem could come from it, but I don't think so. – Olórin Aug 07 '16 at 16:34
  • Is there any risk that GCC and Clang produce incompatible C++ object code? In other words, that the library was compiled by Clang so GCC can't find the symbols because they don't mangle the names the same (probably as a safety measure as there are other things that are different too)? Once upon a long time ago (in another millennium), this used to be a problem — I'm not sure whether it is still a problem. I avoid the issue by using a single compiler rather than trying to interwork. – Jonathan Leffler Aug 07 '16 at 16:47
  • Also, have you looked at the various file types with `file` and/or `otool` to see what they contain. Are there dual-architecture files, or are they all single architecture? – Jonathan Leffler Aug 07 '16 at 16:48
  • It happens that GCC and Clang produce incompatible C++ object code, see few posts on this subject on SO. But here I do compile googletest and my library with GCC-6.1.0. The only library that I have is `libgtest.a` and `nm libgtest.a` gives this : https://bitbucket.org/snippets/MisesEnForce/rjB94/nm-libgtesta – Olórin Aug 07 '16 at 17:48
  • `file libgtest.a` tells me `libgtest.a: current ar archive`. – Olórin Aug 07 '16 at 19:17
3

After some discussion in chat, we discovered that the problem lay in 'common' definitions. A simplified version of the code causing trouble follows. The system is Mac OS X (Mavericks and Yosemite).

With common definitions only

vars.h

extern int    chorebox_argc;
extern char **chorebox_argv;
extern char **chorebox_envp;

vars.c

#include "vars.h"
int    chorebox_argc;
char **chorebox_argv;
char **chorebox_envp;

main.c

#include "vars.h"

int main(int argc, char **argv, char **envp)
{
    chorebox_argc = argc;
    chorebox_argv = argv;
    chorebox_envp = envp;
    return argc;
}

Compilation 1

$ gcc -c vars.c
$ nm vars.o
0000000000000004 C _chorebox_argc
0000000000000008 C _chorebox_argv
0000000000000008 C _chorebox_envp
$ ar rv libvars.a vars.o
ar: creating archive libvars.a
a - vars.o
$ ranlib libvars.a
warning: ranlib: warning for library: libvars.a the table of contents is
empty (no object file members in the library define global symbols)
$ gcc -c main.c
$ gcc -o program main.o vars.o
$ gcc -o program main.o -L. -lvars
Undefined symbols for architecture x86_64:
  "_chorebox_argc", referenced from:
      _main in main.o
  "_chorebox_argv", referenced from:
      _main in main.o
  "_chorebox_envp", referenced from:
      _main in main.o
ld: symbol(s) not found for architecture x86_64
collect2: error: ld returned 1 exit status
$

Note the C in the output from nm. That indicates a 'common' definition of the variables. It isn't enough, on its own, to make them into variable definitions — witness the message from ranlib.

I'm not sure if this is new behaviour in Mac OS X or not. However, it seems that having a source file that only defines uninitialized variables isn't sufficient when the variables are defined in a library, though it is sufficient when the object file is linked directly.

With variable definitions

vardefs.c

#include "vars.h"
int    chorebox_argc = 0;
char **chorebox_argv = 0;
char **chorebox_envp = 0;

This has explicitly initialized versions of the variables. The initializer values are the same as the default values, but the explicit initialization makes all the difference.

Compilation 2

$ rm libvars.a
$ gcc -c vardefs.c
$ ar rv libvars.a vardefs.o
ar: creating archive libvars.a
a - vardefs.o
$ gcc -o program main.o -L. -lvars
$ 

The explicitly initialized variables are picked up from the library without problem.

With one variable definition

vars.h

extern int    chorebox_argc;
extern char **chorebox_argv;
extern char **chorebox_envp;
extern int make_believe;

vars.c

#include "vars.h"
int    chorebox_argc;
char **chorebox_argv;
char **chorebox_envp;

int make_believe = 59;

main.c

#include "vars.h"

int main(int argc, char **argv, char **envp)
{
    chorebox_argc = argc;
    chorebox_argv = argv;
    chorebox_envp = envp;
    make_believe  = 1;
    return argc;
}

Compilation 3

$ gcc -c vars.c
$ ar rv libvars.a vars.o
ar: creating archive libvars.a
a - vars.o
$ nm vars.o
0000000000000004 C _chorebox_argc
0000000000000008 C _chorebox_argv
0000000000000008 C _chorebox_envp
0000000000000000 D _make_believe
$ gcc -c main.c
$ gcc -o program main.o -L. -lvars
$ 

Note that adding the initialized make_believe is sufficient to pull the object file from the library, and the common definitions for the other variables are then sufficient to satisfy the linker.

Lessons

Although the linking order was part of the problem, it was not the whole problem.

Providing uninitialized global variables in a library doesn't always work, especially if there are no other definitions in the same source file.

As I noted in the chat, it is generally not a good idea to provide direct access to global variables. It would be better to provide functional interfaces to access (get and set) the variables.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Thank you so much. It also might be solution to flat namespace error if someone is using python cffi library. Errors there are not too informative there, and it took me way too long realize I never made an archive. – balbok Feb 28 '20 at 22:42