linux 定位段错误的一个方法

背景:程序出现段错误,没有core文件产生,日志没来得及写堆栈信息。

步骤1:使用dmesg查看系统运行信息。

cstaspee[10259]: segfault at 4 ip 00007fdb92acd1df sp 00007fd9c5e65d20 error 6 in libcsta_scm.so[7fdb92aba000+1c000]

发现在cstaspee在调用libcsta_scm.so动态库出现段错误。

步骤2:计算错误点在动态库的地址

7fdb92aba000为模块(libcsta_scm.so)载入地址,
00007fdb92acd1df为程序崩溃点ip指令地址,
相减   00007fdb92acd1df - 7fdb92aba000 = 131DF,
差值为错误点在动态库的地址。

步骤3:使用objdump命令反汇编 或 addr2line

objdump -d libcsta_scm.so > /tmp/xxx.txt

xxx.txt部分文件内容

00000000000130df <_Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii>:
   130df:    55                       push   %rbp
   130e0:    48 89 e5                 mov    %rsp,%rbp
   130e3:    48 83 ec 30              sub    $0x30,%rsp
   130e7:    48 89 7d e8              mov    %rdi,-0x18(%rbp)
   130eb:    89 75 e4                 mov    %esi,-0x1c(%rbp)
   130ee:    89 55 e0                 mov    %edx,-0x20(%rbp)
   130f1:    89 4d dc                 mov    %ecx,-0x24(%rbp)
   130f4:    c7 45 fc 00 00 00 00     movl   $0x0,-0x4(%rbp)
   130fb:    e9 16 02 00 00           jmpq   13316 <_Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii+0x237>
   13100:    48 8b 0d d1 8e 20 00     mov    0x208ed1(%rip),%rcx        # 21bfd8 <[email protected]@Base-0x428>
   13107:    8b 45 fc                 mov    -0x4(%rbp),%eax
   1310a:    48 63 d0                 movslq %eax,%rdx
   1310d:    8b 45 e4                 mov    -0x1c(%rbp),%eax
   13110:    48 63 f0                 movslq %eax,%rsi
   13113:    48 89 d0                 mov    %rdx,%rax
   13116:    48 c1 e0 03              shl    $0x3,%rax
   1311a:    48 01 d0                 add    %rdx,%rax
   1311d:    48 c1 e0 06              shl    $0x6,%rax
   13121:    48 69 d6 d8 39 28 00     imul   $0x2839d8,%rsi,%rdx
   13128:    48 01 d0                 add    %rdx,%rax
   1312b:    48 01 c8                 add    %rcx,%rax
   1312e:    48 05 c0 09 04 00        add    $0x409c0,%rax
   13134:    8b 40 04                 mov    0x4(%rax),%eax
   13137:    83 f8 01                 cmp    $0x1,%eax
   1313a:    0f 85 8e 00 00 00        jne    131ce <_Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii+0xef>
   13140:    8b 45 fc                 mov    -0x4(%rbp),%eax
   13143:    48 63 d0                 movslq %eax,%rdx
   13146:    48 89 d0                 mov    %rdx,%rax
   13149:    48 c1 e0 03              shl    $0x3,%rax
   1314d:    48 01 d0                 add    %rdx,%rax
   13150:    48 c1 e0 06              shl    $0x6,%rax
   13154:    8b 55 e4                 mov    -0x1c(%rbp),%edx
   13157:    48 63 d2                 movslq %edx,%rdx
   1315a:    48 69 d2 d8 39 28 00     imul   $0x2839d8,%rdx,%rdx
   13161:    48 01 d0                 add    %rdx,%rax
   13164:    48 8d 90 c0 09 04 00     lea    0x409c0(%rax),%rdx
   1316b:    48 8b 05 66 8e 20 00     mov    0x208e66(%rip),%rax        # 21bfd8 <[email protected]@Base-0x428>
   13172:    48 01 d0                 add    %rdx,%rax
   13175:    48 83 c0 0c              add    $0xc,%rax
   13179:    0f b6 00                 movzbl (%rax),%eax
   1317c:    84 c0                    test   %al,%al
   1317e:    74 4e                    je     131ce <_Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii+0xef>
   13180:    48 8b 0d 51 8e 20 00     mov    0x208e51(%rip),%rcx        # 21bfd8 <[email protected]@Base-0x428>
   13187:    8b 45 fc                 mov    -0x4(%rbp),%eax
   1318a:    48 63 d0                 movslq %eax,%rdx
   1318d:    8b 45 e4                 mov    -0x1c(%rbp),%eax
   13190:    48 63 f0                 movslq %eax,%rsi
   13193:    48 89 d0                 mov    %rdx,%rax
   13196:    48 c1 e0 03              shl    $0x3,%rax
   1319a:    48 01 d0                 add    %rdx,%rax
   1319d:    48 c1 e0 06              shl    $0x6,%rax
   131a1:    48 69 d6 d8 39 28 00     imul   $0x2839d8,%rsi,%rdx
   131a8:    48 01 d0                 add    %rdx,%rax
   131ab:    48 01 c8                 add    %rcx,%rax
   131ae:    48 05 00 0a 04 00        add    $0x40a00,%rax
   131b4:    0f b7 40 0c              movzwl 0xc(%rax),%eax
   131b8:    0f b7 c0                 movzwl %ax,%eax
   131bb:    89 c7                    mov    %eax,%edi
   131bd:    e8 8e 0d ff ff           callq  3f50 <[email protected]>
   131c2:    83 f8 01                 cmp    $0x1,%eax
   131c5:    75 07                    jne    131ce <_Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii+0xef>
   131c7:    b8 01 00 00 00           mov    $0x1,%eax
   131cc:    eb 05                    jmp    131d3 <_Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii+0xf4>
   131ce:    b8 00 00 00 00           mov    $0x0,%eax
   131d3:    84 c0                    test   %al,%al
   131d5:    0f 84 37 01 00 00        je     13312 <_Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii+0x233>
   131db:    48 8b 45 e8              mov    -0x18(%rbp),%rax
   131df:    c7 40 04 01 00 00 00     movl   $0x1,0x4(%rax)
   131e6:    48 8b 45 e8              mov    -0x18(%rbp),%rax
   131ea:    c7 80 08 01 00 00 00     movl   $0x0,0x108(%rax)

在xxx.txt文件中查找步骤2的地址131DF,
可以知道问题出在csta_scm_baseServiceEventSetup
(echo _Z30csta_scm_baseServiceEventSetupP13STRUCT_CM_MSGiii |   c++filt     )

或者直接使用addr2line命令查看错误出在哪个文件哪一行。

addr2line -e libcsta_scm.so 131df

总结

dmesg 查看崩溃的信息,计算崩溃地址。

objdump或addr2line 找出断点所在的文件及位置。

发表在 C/C++ | 标签为 , , , , | 留下评论

wordpress 502


1.问题

搬瓦工逐步淘汰OpenVZ,OpenVZ 不能续费,只能重新选购KVM。意味着自己的wordpress要迁移到新的VPS。https://bandwagonhost.com/

安装完lnmp,数据库恢复、wordpress相关文件copy到新虚机,nginx配置,DNS解析,可以顺利访问网站,但是登陆后台部分页面出现502。

2.定位

nginx/1.14.0、mysql/5.5.60、php/ 5.6.36版本都是和原来一致的,试了网上的一些方法后都不行。然后自己乖乖的定位。 通过dmesg命令或 查看/var/log/syslog,发现php-fpm在调用libssl.so.1.1时出现段错误。

迁移前后的系统存在着差异,迁移前用的ubuntu 16.04,迁移后ubuntu 18.04,libssl版本一个1.0一个1.1,存在这差异。

3.解决

最后的解决方案,重新安装lnmp环境,php版本选择PHP 7.0.30,运行正常。
PHP 7.0.30依然依赖libssl.so.1.1但不会像5.5.60那样出现段错误,502页面也不出现了。


4.总结

以后干脆搞搞整成docker image得了。

5.题外话

迁移的时候看了两眼access.log,发现有人弱口令爆破自己的网站,哈哈哈。这小破站也值得搞?就怕贼惦记啊。
正好一直找不到好的弱口令集合,打算整理一下,整个弱口令集。

发表在 wordpress | 标签为 , , , , , | 留下评论

linux join命令

服务器上起了一堆服务,刚来公司没多久还搞不清谁和谁交互,设计文档/架构文档,什么?没有!服务都是通过socket通讯的,为了可以分别部署在多台物理服务器上。于是乎想基于netstat命令统计一下交互关系,肉眼看太费劲,用awk搞了半天最后放弃了。

最后发现一个join命令,很好使,记录一下。

netstat -tp | awk '{ print $4$5,$7 }' |sort -k 1 > netstat1.txt
netstat -tp | awk '{ print $5$4,$7 }' |sort -k 1 > netstat2.txt
join -1 1 -2 1 netstat1.txt netstat2.txt | \
		cut -d" " -f2,3 | sed 's#[0-9/\.]##g' |sort -u

前两行命令把第4,5,7列输出到netstat1.txt和netstat2.txt里,不同的是Local Address(4列)和Foreign Address(5列),netstat1.txt和netstat2.txt里调换下位置,而且4,5列合并在一起了。如果netstat1.txt和netstat2.txt第一列相同说明两个程序在交互。

接下来解释join命令大显身手的时候了,像sql语句里的join一样, 命令join把文件结合在一起根据: -1 1指的是第一个文件的第一列,-2 1指的是第二个文件 的第一列。cut -d” ” -f2,3只取第2,3列,sed ‘s#[0-9/.]##g’删除pid和. ,sort -u排序去重。

这样就可以一目了然smserver在和谁交互、imlprocess在和谁交互。

发表在 shell | 标签为 , | 留下评论

sprintf 段错误

最近迁移代码测试功能的时候发现一个段错误,打印堆栈显示std::string析构时段错误。最后分析原因:sprintf造成字符数组内存溢出,踩内存,导致附近std::string对象被改修,析构时产生段错误。

原代码

if(response_code == 200)
{
	sprintf(temp,"HTTP请求成功[%s]", strResponce.c_str());
	dispPlayMsg("HTTP请求",index,temp,WEB_LOG);
	
	return TRUE;
}

strResponce为http的响应结果,平时的都是本公司的soap服务,返回
字节数都很少,测试时的服务返回长度超过temp长度,溢出,踩内存。

修改后

if(response_code == 200)
{
	//sprintf易造成越界,导致段错误
	snprintf(temp, sizeof(temp), "HTTP请求成功[%s]", strResponce.c_str());
	dispPlayMsg("HTTP请求",index,temp,WEB_LOG);

	return TRUE;
}

慎用sprintf,尽量使用snprintf。
原来工作中就遇到过sprintf造成的段错误。

发表在 C/C++ | 标签为 , | 留下评论

printf/sprintf/NULL 空指针问题

最近从windows到linux 迁移代码,遇到了一个sprintf参数空指针段错误。

很疑惑,按说项目这么久不该出现段错误。

1.代码

原代码(用到msxml)

sprintf(pItemData.szNodeText,"%s",(const char*)nodeptr->Gettext());

移植后的代码(用到libxml2)

xmlCharTemp = xmlNodeGetContent(xmlChildNode);

sprintf(pItemData.szNodeText,"%s", (const char*)xmlCharTemp);

xmlFree(xmlCharTemp);

用什么xml解析库并不关键,关键是sprintf的参数都有可能是空指针。

于是乎实验了一下,发现vc++6.0 和 g++编译器存在差异性。

 

 

Windows-vc++6.0-sprintf截图

 

 

Linux-g++sprintf-截图

2.G++为什么会出现这种情况?

输出汇编代码

g++ test.cpp -S -o test.S

 .file "test.cpp"
.section .rodata
.LC0:
.string "%s"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movq $0, -8(%rbp)
leaq -32(%rbp), %rax
movl $0, %edx
movl $.LC0, %esi
movq %rax, %rdi
movl $0, %eax
call sprintf
leaq -32(%rbp), %rax
movq %rax, %rdi
call puts
movq -8(%rbp), %rdx
leaq -32(%rbp), %rax
movq %rdx, %rsi
movq %rax, %rdi
call strcpy
leaq -32(%rbp), %rax
movq %rax, %rdi
call puts
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-28)"
.section .note.GNU-stack,"",@progbits


会发现两次调用sprintf,第一次调用的call sprintf,第二次调用的call strcpy。

3.printf也会存在类似的段错误

Windows-vc++6.0-printf截图

 

Linux-g++-printf-截图

 

g++汇编代码

.file "test2.cpp"
.section .rodata
.LC0:
.string "%s"
.LC1:
.string "%s \n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
movq $0, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rsi
movl $.LC0, %edi
movl $0, %eax
call printf
movq -8(%rbp), %rax
movq %rax, %rsi
movl $.LC1, %edi
movl $0, %eax
call printf
movq -8(%rbp), %rax
movq %rax, %rdi
call puts
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-28)"
.section .note.GNU-stack,"",@progbits

会发现前两次调用的call printf,而第三次调用call puts。

 

4.总结

尽量不要期待编译器去做空指针的判断优化,因为大多数情况下你不知道编译器是怎么实现的,况且编译器间存在差距,自己保证参数的不为空指针。

 

最后代码无奈改成了这个样子。

sprintf(pItemData.szNodeText,"%s", xmlCharTemp!=NULL?(const char*)xmlCharTemp:"(null)");

参考:https://stackoverflow.com/questions/11589342/what-is-the-behavior-of-printing-null-with-printfs-s-specifier

发表在 C/C++ | 标签为 , , | 留下评论

编译链接“ undefined reference to ”

最近在移植一个项目,从windows移植到Linux,在解决掉编译前期问题后,在链接的时候碰到了各种的“ undefined reference to ”问题。

检查所依赖的.h文件是否 #include <>

首先检查所依赖的.h文件是否 #include“xxx.h”

链接时缺少了相关的目标文件(.o)或者库文件(.so)

造成这种情况的原因比较多:

忘记指定目标文件(.o);

忘记指定链接库(-L /xxx/yyy/  -lA);

遗漏指定链接库(B动态库依赖A动态库,链接忘记指定A动态库 -lA)

fun.h

#ifndef __FUN_H__
#define __FUN_H__

int fun();

#endif

fun.cpp

#include "fun.h"
int fun()
{
	return 0;
}

fun1.h

#ifndef __FUN1_H__
#define __FUN1_H__

int fun1();

#endif

fun1.cpp

#include "fun.h"

int fun1()
{
	return fun();
}

缺少链接库

[email protected]:~/test# g++ -fpic -shared fun.cpp -o libfun.so
[email protected]:~/test# g++ -fpic -shared fun1.cpp -o libfun1.so
[email protected]:~/test# g++ main.cpp -o main -L ./ -I ./ -lfun1
.//libfun1.so: undefined reference to `fun()'
collect2: error: ld returned 1 exit status

正确的编译

[email protected]:~/test# g++ -fpic -shared fun.cpp -o libfun.so
[email protected]:~/test# g++ -fpic -shared fun1.cpp -o libfun1.so
[email protected]:~/test# g++ main.cpp -o main -L ./ -I ./ -lfun1 -lfun
[email protected]:~/test#

继续阅读

发表在 编译 | 标签为 , , | 留下评论

网络摄像头 弱口令扫描

确定想扫描的IP范围

首先要确定要扫描的ip范围,可以自己定手写,我是按地域从网站上爬取的。

#coding:utf-8
import requests
from bs4 import BeautifulSoup
from urllib import quote
import re
place_name = "日本"
url_ = 'http://ip.yqie.com/search.aspx?searchword=' + quote(place_name) + "&pagecurrent="
pagecount = re.findall('页码:1/(\d*?)<',requests.get(url_+'1').content)
index = 1
fp = open('ip_range.txt','a')
while index < int(pagecount[0]):
    url = url_ + str(index)
    page = requests.get(url)
    soup = BeautifulSoup(page.content,'lxml').find_all("tr")
    j = 0
    for i in soup:
        if j == 0:
            j = 1
            continue
        fp.write(i.contents[1].contents[0]  + '    ' + i.contents[3].contents[0] + '\n')
    index = index + 1
fp.close()

生成以下格式的文本:
171.105.32.0 171.105.33.255
171.105.34.0 171.105.35.255
171.105.36.0 171.105.36.255
171.105.37.0 171.105.38.255
171.105.39.0 171.105.79.255

继续阅读

发表在 杂项 | 标签为 , , | 留下评论

wordpress 恶意访问

在vps上抓包分析telegram协议时,意外发现有人尝试登录我的wordpress后台,很是意外,看来安全意识不能没有啊。

然后我装了个wordpress插件simple login log来记录登录日志,实验发现并不能记录登录失败的日志,所以看了下simple login log插件源码,simple-login-log.php文件中有以下代码行,

if( isset($this->opt['failed_attempts']) )

因为对php语言不是很了解,也没太仔细看opt在哪初始化或者修改的,索性直接把以上代码全替换成

if( 1 )

估计simple login log作者这么做是怕有攻击者对你网站实施密码爆破,使得mysql数据库爆掉。

而后就能看到登录失败的日志了。

仔细观察一下时间,你会发现这些人还蛮专业,隔两个小时尝试一次,怕短时间内多次访问被禁掉。

有一些插件可以做一些防护,比如:WP Limit Login Attempts、WP-Ban等,但考虑到本来vps内存cpu呀就不够用的,我就没安装这些插件了黑客还是蛮多的,不过我这小站也没啥,任他们去吧

 

发表在 wordpress | 标签为 | 留下评论

Android QQ/微信 数据库

QQ和微信数据本地数据库,可以获取聊天信息以及好友信息等。

QQ

安卓手机QQ数据库:/data/data/com.tencent.qq/databases/QQ号.db(手机root后可获取 RE浏览器);
数据库文件为sqlite数据库;
私聊天记录放在表:mr_friend_MD5(QQ号)_New
群聊天记录放在表:mr_troop_MD5(群号)_New

sqlcipher.exe打开数据库发现一些信息乱码,经过加密。加密方法循环异或IMEI号。

解密demo:

# -*- coding: utf-8 -*-
import sqlite3

IMEI="866536022175869"
conn = sqlite3.connect("971774262.db")
cursor = conn.execute("SELECT frienduin,selfuin,senderuin,msgdata  from mr_troop_158C59D128304F55302B275E6427CA1E_New ")
print "select database successfully";
print "群号\t\t己方QQ\t\t发送方QQ\t\t聊天内容"
for row in cursor:
    a= row[0]
    #print a
    sbstr=""
    for i in range(0,len(a)):
        sbstr+=chr(ord(a[i])^ord(IMEI[i%15]))
    sbstr+="\t"
    a= row[1]
    for i in range(0,len(a)):
        sbstr+=chr(ord(a[i])^ord(IMEI[i%15]))
    sbstr+="\t"
    a= row[2]
    for i in range(0,len(a)):
        sbstr+=chr(ord(a[i])^ord(IMEI[i%15]))
    sbstr+="\t"
    a= row[3]
    for i in range(0,len(a)):
        sbstr+=chr(ord(a[i])^ord(IMEI[i%15]))
    print sbstr

微信

安卓微信数据库:/data/data/com.tencent.mm/MicroMsg    EnMicroMsg.db (需要ROOT)

数据库整个加密,可以用sqlcipher.exe软件直接打开,也可以自己写代码。

数据库密码:拼接IMEI和uin,通过md5加密后,取前7位小写的字符串

uin获取:/data/data/com.tencent.mm/shared_prefs/auth_info_key_prefs.xml (需要ROOT)

发表在 杂项 | 标签为 , | 留下评论

C/C++ 多动态库标识符冲突

场景:
最近在写一个程序的时候,需要调A、B两个服务的客户端动态库,两个客户端动态库中函数有重名(更确切的是重定义)的情况。现象,单写A客户端demo程序运行正常,但集成到程序中死活连不上A服务。


原因:
A、B两个客户端动态库中函数有重定义现象。即都有funcX(int arg)(虽然funcX不是对外接口),但执行过程中A动态库调用了B的funcX,导致程序不正常。而单写A的客户端demo时只用A的动态库,A动态库还是用的A中的funcX,程序正常。

简单来说就是违反了单一定义原则One Definition Rule)。

简单示例说明

fun1.h

#include <stdio.h>
int print1();

fun1.cpp

#include "fun1.h"
int printfun()
{
    printf("1\n");
}
int print1()
{
    printfun();
    return 0;
}

fun2.h

#include <stdio.h>
int print2();

fun2.cpp

#include "fun2.h"
int printfun()
{
    printf("2\n");
}
int print2()
{
    printfun();
    return 0;
}

测试程序test.cpp

#include "fun1.h"
#include "fun2.h"

int main()
{
    print1();
    print2();
    return 0;
}

Makefile

test: libfun1.so libfun2.so
    g++ test.cpp -o test -L ./ -lfun1 -lfun2
libfun1.so:
    g++ fun1.cpp -shared -fpic -o libfun1.so
libfun2.so:
    g++ fun2.cpp -shared -fpic -o libfun2.so
clean:
    rm  -rf libfun1.so libfun2.so test

执行结果

可以看到我们本意是希望一个打印1一个打印2,结果全部打印的1

解决方案1(-Wl,-Bsymbolic )(不推荐)

创建动态链接库时,添加编译选项-Wl,-Bsymbolic,其中Wl表示将紧跟其后的参数,传递给连接器ld。首先看一下-Bsymbolic,    ld的man手册(意思就是优先使用链接库内的定义实现)

-Bsymbolic
When creating a shared library, bind references to global symbols to the definition within the shared library, if any.  Normally, it is possible for a program linked against a shared library to override the definition within the shared library.  This option can also be used with the –export-dynamic option, when creating a position independent executable, to bind references to global symbols to the definition within the executable.  This option is only meaningful on ELF platforms which support shared libraries and position independent executables.

修改Makefile

test: libfun1.so libfun2.so
    g++ test.cpp -o test -L ./ -lfun1 -lfun2
libfun1.so:
    g++ fun1.cpp -shared -fpic -o libfun1.so -Wl,-Bsymbolic
libfun2.so:
    g++ fun2.cpp -shared -fpic -o libfun2.so -Wl,-Bsymbolic
clean:
    rm  -rf libfun1.so libfun2.so test

方案一:执行结果


解决方案2( -fvisibility=hidden )

创建动态库的时,添加编译选项-fvisibility=hidden
首先看一下  -fvisibility=default|internal|hidden|protected,gcc的man手册.

-fvisibility=default|internal|hidden|protected

Set the default ELF image symbol visibility to the specified option—all symbols are marked with this unless overridden within the code. Using this feature can very substantially improve linking and load times of shared object libraries, produce more optimized code, provide near-perfect API export and prevent symbol clashes(防止标志符冲突).  It is strongly recommended that you use this in any shared objects you distribute(强烈建议使用在你写的共享库).

修改头文件
fun1.h

#include <stdio.h>
__attribute__ ((visibility("default"))) int print1();

fun2.h

#include <stdio.h>
__attribute__ ((visibility("default"))) int print2();

修改Makefile

test: libfun1.so libfun2.so
    g++ test.cpp -o test -L ./ -lfun1 -lfun2
libfun1.so:
    g++ fun1.cpp -shared -fpic -o libfun1.so -fvisibility=hidden
libfun2.so:
    g++ fun2.cpp -shared -fpic -o libfun2.so -fvisibility=hidden
clean:
    rm  -rf libfun1.so libfun2.so test

执行结果

解决方案3( 命名空间 )

严格使用命名空间。

fun1.h

#include <stdio.h>
namespace fun1 {
    
int print1();

}

fun1.cpp

#include "fun1.h"
namespace fun1 {
int printfun()
{
    printf("1\n");
}
int print1()
{
    printfun();
    return 0;
}
}

fun2.h

#include <stdio.h>
namespace fun2 {
    
int print2();

}

fun2.cpp

#include "fun2.h"
namespace fun2 {
int printfun()
{
    printf("2\n");
}
int print2()
{
    printfun();
    return 0;
}
}

test.cpp

#include "fun1.h"
#include "fun2.h"

int main()
{
    fun1::print1();
    fun2::print2();
    return 0;
}

Makefile

test: libfun1.so libfun2.so
    g++ test.cpp -o test -L ./ -lfun1 -lfun2
libfun1.so:
    g++ fun1.cpp -shared -fpic -o libfun1.so
libfun2.so:
    g++ fun2.cpp -shared -fpic -o libfun2.so
clean:
    rm  -rf libfun1.so libfun2.so test

执行结果

范例(leveldb)

项目中CMakeLists.txt片段,当编成动态库时添加编译选项-fvisibility=hidden

if(BUILD_SHARED_LIBS)
  # Only export LEVELDB_EXPORT symbols from the shared library.
  add_compile_options(-fvisibility=hidden)
endif(BUILD_SHARED_LIBS)

头文件export.h

// Copyright (c) 2017 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#ifndef STORAGE_LEVELDB_INCLUDE_EXPORT_H_
#define STORAGE_LEVELDB_INCLUDE_EXPORT_H_

#if !defined(LEVELDB_EXPORT)

#if defined(LEVELDB_SHARED_LIBRARY)
#if defined(_WIN32)
//Windows 系列
#if defined(LEVELDB_COMPILE_LIBRARY)
#define LEVELDB_EXPORT __declspec(dllexport)
#else
#define LEVELDB_EXPORT __declspec(dllimport)
#endif  // defined(LEVELDB_COMPILE_LIBRARY)

#else  // defined(_WIN32)
//Linux gcc系列
#if defined(LEVELDB_COMPILE_LIBRARY)
#define LEVELDB_EXPORT __attribute__((visibility("default")))
#else
#define LEVELDB_EXPORT
#endif
#endif  // defined(_WIN32)

#else  // defined(LEVELDB_SHARED_LIBRARY)
#define LEVELDB_EXPORT
#endif

#endif  // !defined(LEVELDB_EXPORT)

#endif  // STORAGE_LEVELDB_INCLUDE_EXPORT_H_

头文件db.h片段

...
namespace leveldb {
...
class LEVELDB_EXPORT DB {
...
};
...
}

总结

不推荐使用第一种做法的原因:你是首先用自己定义的标志符,但别人不一定这么做了,有可能其他的动态库没有添加优先使用本动态库标志符的选项。

自己提供动态库时,规范做法应该如leveldb项目,严格使用命名空间,并且使用-fvisibility=hidden,只导出要导出的定义,其他的隐藏。

发表在 C/C++, 编译 | 标签为 , , , | 留下评论