2


I am working in a big project. And now encounter a link error.
This error can be avoided by a workaround but I can not figure out why it works.

Here is the file structure related to my problem:

project
  |-package_a
    |--a.cpp
    |--...
  |-package_b
    |--b.cpp
    |--c.cpp
    |--...
  |-package_others


All the *.o in package_a will be packed in to a.a and *.o in package_b will be packed into b.a

"g++ -o exec -Bstatic b.a a.a ..." is used to generate the binary.

In package_b/b.cpp, I added a function foo().
And in package_a/a.cpp, I used this function.

But here I will get a link error saying undefined reference of foo() in a.o
I can verify (by objdump) that foo() is already in b.o.

By changing the link command into "g++ -o exec -Bstatic a.a b.a ...", the binary can be built successfully. I now understand that the linker do care about the order in linkage list. But please understand this is a big project I have no permission to change the project configuration so the original link order must be kept.

Then I added a dummy function bar() in package_b/c.cpp, which do nothing but just calling foo(), then original "g++ -o exec -Bstatic b.a a.a ..." will run through without any link error.

Can anybody show me some light why just adding a dummy function in the same package will work in this case?

I'm using g++ 4.4.4 and linux 2.6.18-194.el5

Any comment will be appreciated

Community
  • 1
  • 1
Yike.Wang
  • 133
  • 1
  • 1
  • 6

4 Answers4

4

This is normal. When linking, the linker goes through the list of object files, finding undefined references which are then satisfied by other object files/libraries coming after it.

You can change this behaviour by either

  • including one of the archives twice, as in

    g++ -o exec a.a b.a a.a
    
  • using the -( construct

    g++ -o exec -( a.a b.a -)
    

But please understand this is a big project I have no permission to change the project configuration so the original link order must be kept.

Tough luck... Maybe the manager or whoever just doesn't want you to use functions in b from a.

Then I added a dummy function bar() in package_b/c.cpp, which do nothing but just calling foo(), then original "g++ -o exec -Bstatic b.a a.a ..." will run through without any link error.

Could be that another function of package_b/c.cpp was already referenced, and the linker took bar() with it (because they are in the same file) and this referenced foo(), which was subsequently included in the output, too. It succeeded, because foo was in b.a too.

jpalecek
  • 47,058
  • 7
  • 102
  • 144
  • `Could be that another function of package_b/c.cpp was already referenced, and the linker took bar() with it (because they are in the same file) and this referenced foo(), which was subsequently included in the output, too. It succeeded, because foo was in b.a too.`
    I still have doubt on it, you mean package_b/c.cpp was already referenced before linker handle a.a? but how could no link error when it reference those symbolic in package_b/c.cpp?
    – Yike.Wang Nov 07 '11 at 13:28
  • @user1033573: When an object file references a symbol from the current library/archive, it just works, you don't need to do anything special. Or have I misunderstood your question? – jpalecek Nov 07 '11 at 13:33
  • You said another function in c.cpp, say bar2() is referenced and the linker took bar() with it. My understanding is that this happens before linker handing a.a, right? But why linker does not complain bar2() is not defined, since b.a is still behind in the link sequence. – Yike.Wang Nov 07 '11 at 13:45
  • @Yike.Wang: This happens *while* processing the archive `b.a`. There could be a file containing `main`, there could be some of the undefined symbols from previous arguments. Anyway, the compiler sees c.cpp (in b.a) and foo.cpp (in b.a), needs for whatever reason c.cpp which in turn requires foo.cpp. a.a has nothing to do with it, except that it later benefits from `foo` already being included in the output. – jpalecek Nov 07 '11 at 22:40
  • I just ran into this. I was perplexed why linking against `*.a` was failing, but linking against `*.a *.a` was not. Thank you for the explanation. I guess it's a side effect of using archives, in which some object files may be immediately needed but others may not. – Wug Jan 23 '14 at 17:09
3

You may like to read up on how linkers work. BTW, -Bstatic flag is unnecessary because .a. object file archives link statically only (as if the list of object files contained in .a was specified on the command line instead of .a).

Alternatively, you can always wrap a list of archives to link with --start-group/ --end-group options to make the linker scan the list of archives multiple times, so that no ordering of archives is required (like MS VC++ does):

g++ -o exec -Wl,--start-group a.a b.a -Wl,--end-group

See man ld:

   -( archives -)
   --start-group archives --end-group
       The archives should be a list of archive files.  They may be either
       explicit file names, or -l options.

       The specified archives are searched repeatedly until no new
       undefined references are created.  Normally, an archive is searched
       only once in the order that it is specified on the command line.
       If a symbol in that archive is needed to resolve an undefined
       symbol referred to by an object in an archive that appears later on
       the command line, the linker would not be able to resolve that
       reference.  By grouping the archives, they all be searched
       repeatedly until all possible references are resolved.

       Using this option has a significant performance cost.  It is best
       to use it only when there are unavoidable circular references
       between two or more archives.
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
  • 1
    This is not how linkers work in general. VC++ can handle dependencies in any order, and I cannot think of any reason why GCC shouldn't be able to do the same thing. – Björn Pollex Nov 07 '11 at 12:15
  • 1
    VC++ is more forgiving in this regard. UNIX linkers can work as VC++ does, but one needs to ask for that, see my update. – Maxim Egorushkin Nov 07 '11 at 12:22
  • @maxim-yegorushkin It would be good to have the option to make it do either. "in any order" is convenient but slower. Cyclic dependencies are always a sign of bad design of either code or library organization. – Eddy Pronk Nov 07 '11 at 12:25
  • 1
    @EddyPronk Does `ld` not have this option already? By default it makes one pass over the list of archives, with `--start-group/ --end-group` it does multiple passes. – Maxim Egorushkin Sep 30 '14 at 15:27
2

GCC, unlike the Visual-C++-linker, requires static libraries to be supplied in an order so that references are defined before they are used. Don't ask me why, but you will always have to check that you are listing the files to be linked in the correct order with GCC.

There is an in-depth explanation here.

Community
  • 1
  • 1
Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • Thank you Pollex, This explained the first solution but not the second one(the dummy function added in c.cpp) – Yike.Wang Nov 07 '11 at 12:20
  • Support for circular dependencies comes at a price: You either need a two-pass algorithm to solve for all symbols (worse performance), or you need to keep backreferences to places where unsolved symbols were (worse code); IMHO the benefit is only small for some, and void for most. IMHO2, a circular dependency is a questionable: If you need both anyways, why not make them one package. – Sebastian Mach Nov 07 '11 at 15:52
0

When you are using a function from a static library, you must on the command line first place the file from which the function is used, then the library where the function is defined. Otherwise, if you place the definition first, gcc (or more specifically, ld) discards the "unused" function. That's how gcc works, sorry.

Alexei Khlebnikov
  • 2,126
  • 1
  • 21
  • 21