When I was trying to make a small application by modifying the existing source, I experienced the phenomenon that ** I got a link error even though I was imitating the existing source configuration **. The content was "multiple definitions", but I was wondering why it did not occur in the existing source and only the person who managed it appeared. However, I thought I knew the approximate cause, so I will leave it as a memo.
Windows10 / WSL + Ubuntu 18 + gcc7.4 + binutils 2.3 etc.
The set of sources used for verification is listed below in the github repository. https://github.com/angel-p57/qiita-sample/tree/master/linkerr
Suppose you originally had an app called ʻappok.exe` that had the source dependencies shown in the Makefile (extracting only the relevant parts):
Makefile(Former)
appok.exe: appok.o specific.o libcommon.a
gcc $(LDFLAGS) -o appok.exe appok.o specific.o -L. -lcommon
appok.o: appok.c
gcc -c -o appok.o appok.c
specific.o: specific.c
gcc -c -o specific.o specific.c
libcommon.a: common1.o common2.o
ar r libcommon.a common1.o common2.o
common1.o: common1.c
gcc -c -o common1.o common1.c
common2.o: common2.c
gcc -c -o common2.o common2.c
appok.exe build / execute
$ make appok.exe
gcc -c -o appok.o appok.c
gcc -c -o specific.o specific.c
gcc -c -o common1.o common1.c
gcc -c -o common2.o common2.c
ar r libcommon.a common1.o common2.o
ar: creating libcommon.a
gcc -o appok.exe appok.o specific.o -L. -lcommon
$ ./appok.exe
itest1=1, itest2=2, idup=3
I added the Makefile description and source for ʻappng.exe, which imitated ʻappok.exe
, and ran the build.
Makefile(Additions)
appng.exe: appng.o specific.o libcommon.a
gcc $(LDFLAGS) -o appng.exe appng.o specific.o -L. -lcommon
appng.o: appng.c
gcc -c -o appng.o appng.c
appng.exe build
$ make appng.exe
gcc -c -o appng.o appng.c
gcc -o appng.exe appng.o specific.o -L. -lcommon
./libcommon.a(common2.o):(.bss+0x0): multiple definition of `idup'
specific.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:5: recipe for target 'appng.exe' failed
make: *** [appng.exe] Error 1
However, as shown above, the link failed due to the duplicate definition of the symbol ʻidup`.
I will repost the error details.
make log excerpt
gcc -o appng.exe appng.o specific.o -L. -lcommon
./libcommon.a(common2.o):(.bss+0x0): multiple definition of `idup'
specific.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
The direct cause is that I have duplicated and linked ʻidup in multiple sources as in the error message. However, this ʻidup
was not defined in the newly created ʻappng.c. It was included in the source
specific.c of the linked object and
common2.c(the object is archived in
libcommon.a`) as follows:
Source containing idup
$ grep idup *.c
common2.c:int idup = 0;
specific.c:int idup = 3;
specific.c: printf("itest1=%d, itest2=%d, idup=%d\n", itest1, itest2, idup);
In other words, even though it was ** originally a situation where symbol duplication occurs **, it was ** not manifested **.
Now, let's use the nm
command to see the status of the symbols in each intermediate file that can be built.
nm command output
$ nm specific.o libcommon.a
specific.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T ftest
0000000000000000 D idup
U itest1
U itest2
U printf
libcommon.a:
common1.o:
0000000000000000 D itest1
common2.o:
0000000000000000 B idup
0000000000000000 D itest2
Looking only at this, the definitions of ʻitest1 and ʻitest2
, which are undefined (U) in specific.o
, are included in libcommon.a
, respectively, common1.o
and common2.o. It needs to be resolved with
, and as a result, it seems inevitable that ʻidup contained in both
common2.oand
specific.o` will cause duplication.
This situation can be seen with the linker flag -M
( -Wl, -M
when passing via gcc).
In fact, when linking ʻappng.exe`, it seems as expected.
appng.exe linker flag specification
$ LDFLAGS=-Wl,-M make appng.exe
gcc -Wl,-M -o appng.exe appng.o specific.o -L. -lcommon
Archive member included to satisfy reference by file (symbol)
./libcommon.a(common1.o) specific.o (itest1)
./libcommon.a(common2.o) specific.o (itest2)
…
However, in the case of ʻappok.exe`, I found that the situation was different.
appok.exe linker flag specification
$ rm -f appok.exe; LDFLAGS=-Wl,-M make appok.exe
gcc -Wl,-M -o appok.exe appok.o specific.o -L. -lcommon
Archive member included to satisfy reference by file (symbol)
./libcommon.a(common1.o) specific.o (itest1)
…
Actually, I haven't loaded common2.o
for symbol resolution of ʻitest2. In other words, it turned out that the symbol duplication of ʻidup
did not occur because common2.o
was not read.
Then, read common2.o
, or not. Where did this difference come from? It was in the original ʻappok.c, ʻappng.c
.
Now let's look at the symbol status, including ʻappok.o, ʻappng.o
, with the nm
command.
See all in nm
$ nm appok.o appng.o specific.o libcommon.a
appok.o:
U _GLOBAL_OFFSET_TABLE_
U ftest
0000000000000000 D itest2
0000000000000000 T main
appng.o:
U _GLOBAL_OFFSET_TABLE_
U ftest
0000000000000000 T main
specific.o:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T ftest
0000000000000000 D idup
U itest1
U itest2
U printf
libcommon.a:
common1.o:
0000000000000000 D itest1
common2.o:
0000000000000000 B idup
0000000000000000 D itest2
ʻAppok.o, unlike ʻappng.o
, already has the ʻitest2` symbol.
This is because (in this verification source) I deliberately omitted the definition of ʻitest2 and created ʻappng.c
.
shell-session:appok.c,appng.c diff
$ diff -u appok.c appng.c
--- appok.c 2020-05-04 13:06:26.593127900 +0900
+++ appng.c 2020-05-04 13:06:26.577501900 +0900
@@ -1,5 +1,4 @@
void ftest(void);
-int itest2 = 2;
int main(void) {
ftest();
So, when you link specific.o
, ʻitest2 is resolved and reading
common2.ois skipped. In other words, you can see that ** the
.o file in the
.a` archive will not be read ** unless it is related to symbol resolution.
On the other hand, if you specify the .o
file directly instead of archiving, it will be read regardless of whether it has been resolved, so ʻappok.exe` will also cause an error.
shell-session:.Without using a.Direct o
$ gcc -o appok.exe appok.o specific.o common1.o common2.o
common2.o:(.data+0x0): multiple definition of `itest2'
appok.o:(.data+0x0): first defined here
common2.o:(.bss+0x0): multiple definition of `idup'
specific.o:(.data+0x0): first defined here
collect2: error: ld returned 1 exit status
In this verification, we found the following two points.
.o
files) are not read from the archive (.a
files). in ʻappok.c
) and skip loading the suspect object to avoid it. Can be done.Personally, it's not a very refreshing story, but as far as the behavior is concerned, I think it's a story like this.