一個令人蛋疼的連結錯誤,蛋疼連結

來源:互聯網
上載者:User

一個令人蛋疼的連結錯誤,蛋疼連結
背景我們APP的引擎包engine.so,包含了A、B、C三個工程,但每次都是源碼形式編譯,導致svn上存在多份相同代碼拷貝。非常不科學。。。核心的B工程由我維護,整個SO編譯工程由多個人維護。於是乎偶進行了一次升級:將B原始碼從so工程中解耦:將B打成一個靜態庫,然後編譯So的時候連結靜態庫。
開始行動基本思路:將B的源碼包到一個guide_b外殼工程中,ndk-build產生guide_b.so 的同時誘導產生libB.a靜態庫,然後這個libB.a發行就緒。android的編譯目錄實在蛋疼,eclipse下設定路徑難用的很。還是習慣命令列下的ndk-build,但是ndk-build的前提是:當前路徑下必須有一個jni檔案夾,且它裡面有一個Android.mk檔案,以及srcxx子目錄,裡面放了原始碼。為了遵循android ndk編譯這個蛋疼的規定,而且又不破壞B工程的項目結構(舊的支援xcode和vs編譯),在build目錄下增加一個android子目錄,建立Android.mk檔案,然後通過python指令碼將所需源碼檔案拷貝到android/jni下,所有這些操作通過一個run.bat批處理指令碼串聯,build完成以後刪掉拷貝的源碼和編譯中間結果。
將python引入編譯非常靈活。
踩雷了拿到編譯出的libB.a,放入engine.so編譯工程中,修改mk檔案,頭部加入靜態庫先行編譯段,

include $(CLEAR_VARS)LOCAL_MODULE := BModuleLOCAL_SRC_FILES := libB.a  include $(PREBUILT_STATIC_LIBRARY)
在so編譯部分載入BModule模組:LOCAL_STATIC_LIBRARIES := BModule編譯so非常順利。但是拿到APP工程中, 傻眼了build以後的包只有地圖文字,沒有底圖了。

淚奔了,libB.a和engine.so的編譯過程都自我感覺非常之良好啊。。。尼瑪。只能不斷自我打擊,暗示一定什麼環節出問題了。。沒有crash,文字標註還有,但是底圖一直渲染不成功。。。


排雷的過程

1)將B的源碼放入SO編譯工程,最終so包沒問題。只能懷疑自己的libB.a編譯有問題或者連結有問題咯,於是進行第一個嘗試:

不直接進行源碼編譯,而是通過ndk內建的arm-linux-androideabi-ar.exe工具,將源碼編譯時間產生的一系列.o檔案,手工編譯成.a,然後連結這個.a,發現build的so包還是有問題。

source =>*.o=>engine.so*.o=>libB.a=>engine.soar
上述兩種路徑:第一條表示源碼編譯,ok;第二條是源碼編譯的中間結果.o檔案,手工通過ar打包成 libB.a,然後連結libB.a,就有問題。 真是見鬼了。!。

2)躲不過了,只能source中增加log,第一次build成libB.a,然後第二次build成engine.so,最後拷貝到android工程中,build APK。

source =>libB.a=>engine.so=>apk.

整個蛋疼的定位過程得益於windows的批處理指令碼,可以實現半自動化。

不斷重複這個過程,不斷調整log精度。最終定位到底圖瓦片繪製失敗的問題:座標轉化函數GetGeoRect的結果錯誤,導致繪製時候取不到資料

定位問題的原因尼瑪,疑問重重:源碼編譯沒問題,build成靜態庫,然後再連結就有問題。代碼沒有改動,為何單單這個核心函數出問題呢?進一步驗證:GetGeoRect是一個類靜態函數,寫一個main.cpp,測試libB.a中的該函數是否正確,顯式傳入制定的參數,build成可執行程式,然後推到手機上執行,具體參見http://blog.csdn.net/ryfdizuo/article/details/28891649 結果函數執行結果正確。。說明libB.a內這些函數本身沒有問題。 問題出在so包的連結階段

跟組內一經驗豐富的哥們討論,那天恰好周五,下班前還是沒結果。。。晚上回去後回一直在回想編譯的整個過程,想起他無心的一句話:“是不是可能有重複的定義啥的“。終於想到了一個問題,A工程裡面B工程的兩個標頭檔,當時為瞭解耦其他人將兩個標頭檔重複拷貝了一份,(明顯觸犯了DRY原則)如下, yy.h中包含了靜態函數的GetGeoRect定義,vv.h中包含了render_config_t結構體定義,而GetGeoRect中使用了render_config_t結構體。


我最近一次B模組升級,更新了vv.h中的render_config_t結構體,內部增加了一個256的char數組。。

附圖新舊vv.h標頭檔中的render_config_t結構體:

舊的:新的:

第二天周六,按耐不住奔到公司,更新A模組中的vv.h標頭檔,build出的so包終於正確了。總算是找到問題所在了:

A和B工程中的vv.h和yy.h檔案重複,B中vv.h檔案最近被更新過。

1)當A、B工程均採用源碼編譯時間,最終SO中的GetGeoRect函數內部使用了最新的render_config_t結構體布局(編譯器可能根據檔案的修改時間等等作為比對條件吧?),因此底圖繪製正確。

2)但是當B工程build成靜態庫libB.a時,此時build成SO時,GetGeoRect函數定義採用了A工程中源碼(編譯器可能更加信賴源碼吧),因此render_config_t也採用了舊的記憶體布局。因此當調用SO運行時,傳入GetGeoRect函數的render_config_t的對象採用最新的記憶體布局,但是內部實際上是按舊的結構體解析和執行,當然結果就完全錯了。


一句話總結問題檔案重複拷貝 =》 導致兩個地方檔案更新不同步 =》導致同一個結構體有兩份定義,兩種記憶體布局 =》 導致SO中的全域函數中,libB.a中沒有被重複定義的函數採用了新的結構體布局,被重複的函數則採用了舊的記憶體布局 =》最終結果:傳入GetGeoRect函數之前的結構體是新布局,函數內部按舊布局解析,所有參數錯亂。
附錄
靜態庫,只是.o檔案的集合打包。動態庫檔案:沒有範圍區分,所有函數都以唯一的全域函數形式存在,C++的函數會被name-mangling處理,如果我們希望直接通過函數名擷取so中的函數地址,則使用extern C包裹防止函數名被修改。通過objdump工具可以驗證。


VC++們過來看看,令人蛋疼的串連錯誤error LNK2019……

這個應該是由於只聲明了Sensed函數而沒有實現就去調用它造成的問題,你實現一下Sensed這個函數,函數體直接return 0 試試!!檢查標頭檔是否匯入,工程目錄是否有載入該檔案。
 
重裝後一個蛋疼的問題

格式化C盤不是問題,你的意思就是把G盤改成C盤的盤符吧,給你個批處理試試看,複製一下代碼到任何一個文字檔,儲存後修改副檔名為.bat
::publised on Nov 20, 2008 and revised on Nov 23, 2008
::ordering drive letters by drive type
::ordered drive letters are assigned to fixed disk, cdrom, usb hard disk and usb stick
@echo off
if %cd:~0,2% neq %systemdrive% (
echo please run it on drive: %systemdrive:~0,1%
pause
exit
)

SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION

::get all drive letters
wmic /? >nul 2>nul
if errorlevel 1 (
for /f "tokens=1 delims= " %%a in ('REG QUERY HKLM\SYSTEM\MountedDevices\ /s ^|find "\DosDevices\"') do (
set adrv=%%a
set adrv=!adrv::=!
set adrv=!adrv:~-1!
call set ALLDRV=%%ALLDRV%% !!adrv!!
)
) else (
for /f "skip=1" %%a in ('wmic logicaldisk get caption') do (
set adrv=%%a
set adrv=!adrv::=!
call set ALLDRV=%%ALLDRV%% !!adrv!!
)
)

echo All drive letters: %ALLDRV%

::get all mounted volume ids and drive letters
FOR /F %%a in ('mountvol ^|find "\"') do call set mtvdrv=%%mtvdrv%% %%a
set mtvdrv=%mtvdrv: =%
set mtvdrv=%mtvdrv:\\?\Volume= %
set mtvdrv=%mtvdrv:\=%
set mtvdrv=%mtvdrv::=%

for %%a in (%mtvdrv%) do (
set mdrv=%%a
set mdrv=!mdrv:~-1!
call set mtdrv=%%mtdrv%% !!mdrv!!
)

echo Mounted drive letters: %mtdrv%

::get virtual drive letters
set virdrv=%ALLDRV%
for %%a in (%mtdrv%) do (
set virdrv=!virdrv:%%a=!
)
set virdrv=!vi......餘下全文>>
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.