编程参考- 重名符号的链接问题

有如下三个文件:

funcA.c:


void bar( ) {

}


void funcA () {

}



funcB.c:


void bar( ) {

}


void funcB () {

}


main.c

int main() {

  return 0;

}

编译:

$ gcc -o test main.c funcA.c funcB.c

/usr/bin/ld: /tmp/cczNqgbd.o: in function `bar':

funcB.c:(.text+0x0): multiple definition of `bar'; /tmp/ccoiI4Pf.o:funcA.c:(.text+0x0): first defined here

collect2: error: ld returned 1 exit status

使用C文件进行编译时,因为bar函数有重复定义,所以链接出错。

把C文件先编译成obj文件,也是一样错误。

$ gcc -c funcA.c

$ gcc -c funcB.c

$ gcc -o test main.c funcA.o funcB.o

/usr/bin/ld: /tmp/cczNqgbd.o: in function `bar':

funcB.c:(.text+0x0): multiple definition of `bar'; /tmp/ccoiI4Pf.o:funcA.c:(.text+0x0): first defined here

collect2: error: ld returned 1 exit status

如果把funcA.c和funcB.c里面的重复定义的bar函数删掉,则能正常编译,没有错误提示。

并且将funcA和funcB两个函数都链接进了可执行文件里,虽然并没有调用。

$ gcc -o test main.c funcA.c funcB.c

$ nm -g test

0000000000004010 B __bss_start

                 w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

00000000000011c8 T _fini

0000000000001138 T funcA

0000000000001143 T funcB

                 w __gmon_start__

0000000000002000 R _IO_stdin_used

                 w _ITM_deregisterTMCloneTable

                 w _ITM_registerTMCloneTable

00000000000011c0 T __libc_csu_fini

0000000000001150 T __libc_csu_init

                 U __libc_start_main@@GLIBC_2.2.5

0000000000001129 T main

0000000000001040 T _start

0000000000004010 D __TMC_END__

然后,我们恢复最初的bar函数定义,funcA.c和funcB.c里都有bar函数定义。

然后编译成obj文件,再把两个文件的obj文件打包成静态库,使用静态库来生成可执行文件。

因为main函数里没有调用两文件中的函数,所以生成的可执行文件里就没包含两个obj文件的符号,这里就和上面的使用源文件或obj文件的结果不一样了。

并且,funcA.o和funcB.o里面的重复的bar函数定义也不会报错,因为这两个obj文件都没有参与链接。

$ gcc -c funcA.c

$ gcc -c funcB.c

$ ar rcs func.a func*.o

$ nm func.a

funcA.o:

0000000000000000 T bar

0000000000000017 T funcA

                 U _GLOBAL_OFFSET_TABLE_

                 U puts

funcB.o:

0000000000000000 T bar

0000000000000017 T funcB

                 U _GLOBAL_OFFSET_TABLE_

                 U puts

$ gcc -o test main.c func.a

$ nm -g test

0000000000004010 B __bss_start

                 w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

00000000000011b8 T _fini

                 w __gmon_start__

0000000000002000 R _IO_stdin_used

                 w _ITM_deregisterTMCloneTable

                 w _ITM_registerTMCloneTable

00000000000011b0 T __libc_csu_fini

0000000000001140 T __libc_csu_init

                 U __libc_start_main@@GLIBC_2.2.5

0000000000001129 T main

0000000000001040 T _start

0000000000004010 D __TMC_END__

注意,关于obj文件的打包:

$ ar rcs func.a func*.o

这个相当于

$ ar rcs func.a funcA.o funcB.o

所以,使用nm命令,显示的顺序就是打包的顺序,先是funcA.o,之后是funcB.o

那如果我们main函数里调用了库里的函数呢?

main.c

int main()

{

  funcA();

  return 0;

}

生成可执行文件:

$ gcc -o test main.c func.a

$ nm -g test

0000000000001162 T bar

0000000000004010 B __bss_start

                 w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

0000000000001208 T _fini

0000000000001179 T funcA

                 w __gmon_start__

0000000000002000 R _IO_stdin_used

                 w _ITM_deregisterTMCloneTable

                 w _ITM_registerTMCloneTable

0000000000001200 T __libc_csu_fini

0000000000001190 T __libc_csu_init

                 U __libc_start_main@@GLIBC_2.2.5

0000000000001149 T main

                 U puts@@GLIBC_2.2.5

0000000000001060 T _start

0000000000004010 D __TMC_END__

可以正确生成,并且包含了被调用的funcA.o里面的funcA函数,另外没有使用的bar函数也包含了进来,也就是说将funcA.o的内容都包含进来了。

funcB.o里面没有内容被调用,就都没有包含,所以bar函数重复也不用管。

再修改一下main函数,调用bar函数。

main.c

int main()

{

  funcA();

  bar();

  return 0;

}

生成可执行文件:

$ gcc -o test main.c func.a

$ nm -g test

000000000000116c T bar

0000000000004010 B __bss_start

                 w __cxa_finalize@@GLIBC_2.2.5

0000000000004000 D __data_start

0000000000004000 W data_start

0000000000004008 D __dso_handle

0000000000004010 D _edata

0000000000004018 B _end

0000000000001208 T _fini

0000000000001183 T funcA

                 w __gmon_start__

0000000000002000 R _IO_stdin_used

                 w _ITM_deregisterTMCloneTable

                 w _ITM_registerTMCloneTable

0000000000001200 T __libc_csu_fini

0000000000001190 T __libc_csu_init

                 U __libc_start_main@@GLIBC_2.2.5

0000000000001149 T main

                 U puts@@GLIBC_2.2.5

0000000000001060 T _start

0000000000004010 D __TMC_END__

结果和前一个例子类似,funcA.o的内容全都包含进来了,包括被调用的funcA和bar函数。

那funcB.o里的bar函数有影响吗?

这里关系到我们打包时的顺序,如果我们先打包funcB.o文件,那链接器在查找main函数要调用的bar函数时,先找到的就是funcB.o。

而查找调用的funcA函数时,找到的就是funcA.o。

那这两个文件就都要参与链接,这时因为bar函数重复定义,就会出错。

$ gcc -c funcA.c

$ gcc -c funcB.c

$ ar rcs func.a funcB.o funcA.o

$ nm func.a

funcB.o:

0000000000000000 T bar

0000000000000017 T funcB

                 U _GLOBAL_OFFSET_TABLE_

                 U puts

funcA.o:

0000000000000000 T bar

0000000000000017 T funcA

                 U _GLOBAL_OFFSET_TABLE_

                 U puts

$ gcc -o test main.c func.a

/usr/bin/ld: func.a(funcA.o): in function `bar':

funcA.c:(.text+0x0): multiple definition of `bar'; func.a(funcB.o):funcB.c:(.text+0x0): first defined here

collect2: error: ld returned 1 exit status

总结:

1,生成可执行文件时,如果是源文件或obj文件参与,则连接过程时,其内容都会被包含进可执行文件。

2,如果是静态库的话,是以里面的obj文件为单位,如果用到了就包含,没用到就不包含。

3,被引用的函数或变量,链接器会在静态库里查找定义,以最先找到的为准,找到后不会继续查找。

查找的顺序,如果是多个静态库,就按照先后顺序。如果是静态库内部,就是obj文件打包的顺序。

4,静态库里可以有重复的定义,只要其所在的obj文件不同时被使用即可。

关于使用静态库参数程序编译

上面的可执行程序的生成,也可以使用如下命令:

$ ar rcs libfunc.a *.o

$ gcc -o test main.c -lfunc -L./

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值