精簡 chromium 源碼後編譯 Android cronet 失敗:undefined symbol: iswblank

0、緣起

由於工作需要,團隊決定自己維護一份 Android cronet 庫的源碼,但由於 chromium 項目代碼過於龐大,需要移除不必要的代碼,移除之後再編譯來測試是否成功編譯出 cronet 庫。
精簡過程中需要刪除一些可能不需要依賴的目錄,以及修改相關的 BUILD.gn 文件,這些改動的後果都是不太能確定的。

1、總結

  1. 錯誤本身跟 android_ndk 版本沒關係,因爲編譯成功和編譯失敗的參數都是一致的

  2. chromium 代碼以及依賴項都是跟系統平臺相關的,因此不要在兩個不兼容平臺之間隨意拷貝源碼

  3. 精簡 chromium 倉庫時對 BUILD.gn 以及相關目錄的增刪必須要用文檔記錄,並做到及時更新,否則重頭做一遍的代價太高。

2、問題定位過程

下面是詳細的錯誤日誌,可以看到第一個錯誤是由於 __posix_l_fallback.h 文件引用的 iswblank 未定義;是在編譯生成目標文件 /libc++/locale.o 時出現的。經過查閱,該方法是一個 c++ 的標準庫函數.

ninja: Entering directory `out/test-Cronet/'
[9/5622] SOLINK ./libnetty-tcnative.so
FAILED: libnetty-tcnative.so libnetty-tcnative.so.TOC lib.unstripped/libnetty-tcnative.so 
python "../../build/toolchain/gcc_solink_wrapper.py" --readelf="../../third_party/android_ndk/toolchains/arm-
linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-readelf" --
nm="../../third_party/android_ndk/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-
androideabi-nm" --strip=../../buildtools/third_party/eu-strip/bin/eu-strip --sofile="./lib.unstripped/libnetty-
tcnative.so" --tocfile="./libnetty-tcnative.so.TOC" --output="./libnetty-tcnative.so" -- ../../third_party/llvm-
build/Release+Asserts/bin/clang++ -shared -Wl,--fatal-warnings -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-
z,now -Wl,-z,defs -Wl,--as-needed --gcc-toolchain=../../third_party/android_ndk/toolchains/arm-linux-
androideabi-4.9/prebuilt/linux-x86_64 -fuse-ld=lld -Wl,--color-diagnostics -Wl,--no-rosegment -Wl,--exclude-
libs=libgcc.a -Wl,--exclude-libs=libvpx_assembly_arm.a --target=arm-linux-androideabi -Werror --
sysroot=../../third_party/android_ndk/platforms/android-16/arch-arm -nostdlib -Wl,--warn-shared-textrel -
L../../third_party/android_ndk/sources/cxx-stl/llvm-libc++/libs/armeabi-v7a -o "./lib.unstripped/libnetty-
tcnative.so" -Wl,-soname="libnetty-tcnative.so" @"./libnetty-tcnative.so.rsp"
ld.lld: error: undefined symbol: iswblank
>>> referenced by __posix_l_fallback.h:79 (../../buildtools/third_party/libc++/trunk/include/support/xlocale/__posix_l_fallback.h:79)
>>>               obj/buildtools/third_party/libc++/libc++/locale.o:(std::__1::ctype_byname<wchar_t>::do_is(unsigned long, wchar_t) const)

ld.lld: error: undefined symbol: iswblank
>>> referenced by __posix_l_fallback.h:79 (../../buildtools/third_party/libc++/trunk/include/support/xlocale/__posix_l_fallback.h:79)
>>>               obj/buildtools/third_party/libc++/libc++/locale.o:(std::__1::ctype_byname<wchar_t>::do_is(wchar_t const*, wchar_t const*, unsigned long*) const)

ld.lld: error: undefined symbol: iswblank
>>> referenced by locale.cpp:1333 (../../buildtools/third_party/libc++/trunk/src/locale.cpp:1333)
>>>               obj/buildtools/third_party/libc++/libc++/locale.o:(std::__1::ctype_byname<wchar_t>::do_scan_is(unsigned long, wchar_t const*, wchar_t const*) const)

ld.lld: error: undefined symbol: iswblank
>>> referenced by __posix_l_fallback.h:79 (../../buildtools/third_party/libc++/trunk/include/support/xlocale/__posix_l_fallback.h:79)
>>>               obj/buildtools/third_party/libc++/libc++/locale.o:(std::__1::ctype_byname<wchar_t>::do_scan_is(unsigned long, wchar_t const*, wchar_t const*) const)

ld.lld: error: undefined symbol: iswblank
>>> referenced by __posix_l_fallback.h:79 (../../buildtools/third_party/libc++/trunk/include/support/xlocale/__posix_l_fallback.h:79)
>>>               obj/buildtools/third_party/libc++/libc++/locale.o:(std::__1::ctype_byname<wchar_t>::do_scan_not(unsigned long, wchar_t const*, wchar_t const*) const)

ld.lld: error: undefined symbol: __ctype_get_mb_cur_max
>>> referenced by __bsd_locale_fallbacks.h:30 (../../buildtools/third_party/libc++/trunk/include/__bsd_locale_fallbacks.h:30)
>>>               obj/buildtools/third_party/libc++/libc++/locale.o:(std::__1::__libcpp_mb_cur_max_l(__locale_t*))
clang: error: linker command failed with exit code 1 (use -v to see invocation)
[11/5622] CXX obj/third_party/googletest/gtest/gtest-port.o
ninja: build stopped: subcommand failed.

我在 mac 平臺和 ubuntu 平臺均嘗試使用了該方法,均能成功執行,意味着 iswblank 在使用默認的系統運行時庫時並非是未定義的,測試程序如下:

// test.cc

#include <iostream>
int main(){
    char ch = 'a';
    if(::iswblank(ch)){
        std::cout << "true iswblank" << std::endl;
    }else{
        std::cout << "false iswblank" << std::endl;
    }
    return 0;
}

此時想到 gn 的編譯往往不用系統的運行時庫,而是依賴 chromium 倉庫中獨立的庫。
在 buildtools/third_party/libc++/ 目錄下找到了其 BUILD.gn 文件,從而追蹤到了 locale.o 的源文件 locale.cpp,在該文件中添加 iswblank 的一行測試代碼導致編譯失敗,此時可以看到編譯 release 的詳細參數:

../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF 
obj/buildtools/third_party/libc++/libc++/locale.o.d -D_LIBCPP_BUILDING_LIBRARY -
D_LIBCPP_OVERRIDABLE_FUNC_VIS=__attribute__\(\(__visibility__\(\"default\"\)\)\) -
DLIBCXX_BUILDING_LIBCXXABI -DNO_TCMALLOC -DSAFE_BROWSING_DB_REMOTE -
DOFFICIAL_BUILD -DCHROMIUM_BUILD -D_GNU_SOURCE -DANDROID -DHAVE_SYS_UIO_H -
DANDROID_NDK_VERSION_ROLL=r16_1 -DCR_CLANG_REVISION=\"357692-1\" -
D_LIBCPP_ABI_UNSTABLE -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -
D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_ENABLE_NODISCARD -
DCR_LIBCXX_REVISION=358423 -D__GNU_SOURCE=1 -DCHROMIUM_CXX_TWEAK_INLINES -
DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -I../.. -Igen -fprofile-sample-
use=../../chrome/android/profiles/afdo.prof -fprofile-sample-accurate -fno-strict-aliasing --param=ssp-buffer-
size=4 -fstack-protector -funwind-tables -fPIC -fcolor-diagnostics -fmerge-all-constants -fcrash-diagnostics-
dir=../../tools/clang/crashreports -Xclang -mllvm -Xclang -instcombine-lower-dbg-declare=0 -flto=thin -fsplit-lto-
unit -fcomplete-member-pointers -ffunction-sections -fno-short-enums --target=arm-linux-androideabi -
isystem../../third_party/android_ndk/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=16 -
DHAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC=1 -march=armv7-a -mfloat-abi=softfp -
mtune=generic-armv7-a -Xclang -fdebug-compilation-dir -Xclang . -no-canonical-prefixes -mfpu=vfpv3-d16 -
mthumb -Oz -fno-ident -fdata-sections -ffunction-sections -fomit-frame-pointer -gdwarf-3 -g1 -fdebug-info-for-
profiling -fvisibility=hidden -Xclang -add-plugin -Xclang find-bad-constructs -Xclang -plugin-arg-find-bad-
constructs -Xclang check-ipc -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -fstrict-
aliasing -fPIC -Werror -Wall -Wno-unused-variable -Wno-missing-field-initializers -Wno-unused-parameter -
Wno-c++11-narrowing -Wno-unneeded-internal-declaration -Wno-undefined-var-template -Wno-ignored-
pragma-optimize -std=c++14 -nostdinc++ -isystem../../buildtools/third_party/libc++/trunk/include -
isystem../../buildtools/third_party/libc++abi/trunk/include --sysroot=../../third_party/android_ndk/sysroot -
isystem../../third_party/android_ndk/sources/android/support/include -fvisibility-inlines-hidden -fexceptions -frtti 
-c ../../buildtools/third_party/libc++/trunk/src/locale.cpp -o obj/buildtools/third_party/libc++/libc++/locale.o

使用上述編譯來編譯 test.cc 得到 test.o;通過 nm 分析 test.o 發現此時 iswblank 是未定義的符號:

# nm -A test.o | grep iswblank
>>> test.o:         U iswblank

因此需要檢查該編譯參數中依賴的庫是否完整或者是版本不一致導致的。
分析編譯參數發現,android_ndk 版本爲 r16_1,而網上有介紹它跟 iswblank 未定義有一定的關聯,需要確認該參數是否可以指定爲其他值,它爲什麼默認設置爲 r16_1 的:

-DANDROID_NDK_VERSION_ROLL=r16_1  -D__ANDROID_API__=16 -march=armv7-a

或者可以對比一下能正確編譯 release 的時候參數是什麼樣的,是否有區別,如果有,是怎麼導致的,如果沒有那麼是否是因爲缺少什麼文件,下面是能正確編譯的詳細參數:

../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF 
obj/buildtools/third_party/libc++/libc++/locale.o.d -D_LIBCPP_BUILDING_LIBRARY -
D_LIBCPP_OVERRIDABLE_FUNC_VIS=__attribute__\(\(__visibility__\(\"default\"\)\)\) -
DLIBCXX_BUILDING_LIBCXXABI -DNO_TCMALLOC -DSAFE_BROWSING_DB_REMOTE -
DOFFICIAL_BUILD -DCHROMIUM_BUILD -D_GNU_SOURCE -DANDROID -DHAVE_SYS_UIO_H -
DANDROID_NDK_VERSION_ROLL=r16_1 -DCR_CLANG_REVISION=\"357692-1\" -
D_LIBCPP_ABI_UNSTABLE -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -
D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_ENABLE_NODISCARD -
DCR_LIBCXX_REVISION=358423 -D__GNU_SOURCE=1 -DCHROMIUM_CXX_TWEAK_INLINES -
DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -I../.. -Igen -fprofile-sample-
use=../../chrome/android/profiles/afdo.prof -fprofile-sample-accurate -fno-strict-aliasing --param=ssp-buffer-
size=4 -fstack-protector -funwind-tables -fPIC -fcolor-diagnostics -fmerge-all-constants -fcrash-diagnostics-
dir=../../tools/clang/crashreports -Xclang -mllvm -Xclang -instcombine-lower-dbg-declare=0 -flto=thin -fsplit-lto-
unit -fcomplete-member-pointers -ffunction-sections -fno-short-enums --target=arm-linux-androideabi -
isystem../../third_party/android_ndk/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=16 -
DHAVE_PTHREAD_COND_TIMEDWAIT_MONOTONIC=1 -march=armv7-a -mfloat-abi=softfp -
mtune=generic-armv7-a -Xclang -fdebug-compilation-dir -Xclang . -no-canonical-prefixes -mfpu=vfpv3-d16 -
mthumb -Oz -fno-ident -fdata-sections -ffunction-sections -fomit-frame-pointer -gdwarf-3 -g1 -fdebug-info-for-
profiling -fvisibility=hidden -Xclang -add-plugin -Xclang find-bad-constructs -Xclang -plugin-arg-find-bad-
constructs -Xclang check-ipc -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -fstrict-
aliasing -fPIC -Werror -Wall -Wno-unused-variable -Wno-missing-field-initializers -Wno-unused-parameter -
Wno-c++11-narrowing -Wno-unneeded-internal-declaration -Wno-undefined-var-template -Wno-ignored-
pragma-optimize -std=c++14 -nostdinc++ -isystem../../buildtools/third_party/libc++/trunk/include -
isystem../../buildtools/third_party/libc++abi/trunk/include --sysroot=../../third_party/android_ndk/sysroot -
isystem../../third_party/android_ndk/sources/android/support/include -fvisibility-inlines-hidden -fexceptions -frtti 
-c ../../buildtools/third_party/libc++/trunk/src/locale.cpp -o obj/buildtools/third_party/libc++/libc++/locale.o

經過對比發現,兩者的參數是一致的。

由於之前刪除過 src/third_party/android_ndk/sources/cxx-stl/llvm-libc++ ,後來爲了方便,我又從 mac 下的 chromium 倉庫的

拷貝 llvm-libc++ 到 cronet 倉庫,mac 下的 chromium 是 android 分支,與維護的是同一個版本;由於涉及不同的平臺,mac 平

臺和 Linux 平臺,可能下載的代碼會有些差別;保險起見,重新從 Linux 下的 chromium 倉庫下載 llvm-libc++,替換現在維護的

倉庫,同時替換 src/ 下的 BUILD.gn 爲初始版本,並刪除 open264、ffmpeg 等的依賴。終於問題不再出現。

因爲做了兩處改動,因此需再次一一驗證,是哪一次改動影響了結果。

首先備份 linux 平臺下的 llvm-libc++,然後拷貝一份 mac 下 chromium 倉庫中的 llvm-c++ 至當前倉庫對應的目錄。

再次編譯,出現下述錯誤,__ctype_get_mb_cur_max 未定義錯誤是由於 llvm-libc++ 的版本與當前編譯的系統平臺不一致導致的,比如在 Linux 下編譯 cronet,需要保證源碼及依賴是拉取的 Linux 平臺(或 Android)的,而非 Mac 或其他平臺的。

ld.lld: error: undefined symbol: __ctype_get_mb_cur_max
>>> referenced by __bsd_locale_fallbacks.h:30 (../../buildtools/third_party/libc++/trunk/include/__bsd_locale_fallbacks.h:30)
>>>               thinlto-cache/Thin-02860b.tmp.o:(std::__1::__libcpp_mb_cur_max_l(__locale_t*))
clang: error: linker command failed with exit code 1 (use -v to see invocation)
[1200/6835] CXX obj/third_party/ced/ced/compact_enc_det.o

所以在編譯 android 版本或者 linux 版本的 cronet 時,需要考慮 linux 平臺下的 chromium 倉庫的源碼,而不要拷貝 mac 平臺的源碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章