cquery最近改動與libclang.so一位元組補丁
原文:maskray.me/blog/2017-12-25-cquery-updates-and-libclang-one-byte-patch
cquery最近改動
cquery介紹參看使用cquery:C++ language server。
最近cquery改動比較多:
- 可執行文件從
build/app
變到build/release/bin/cquery
了,支持release/debug/asan
等多種waf variants,使用RPATH ./waf configure --bundled=5.0.1
可以用上最新出爐的clang+llvm 5.0.1- Riatre把Windows構建修好了 #154
- FreeBSD可以使用了 #155及third party庫改動,感謝ngkaho1234把sparsepp FreeBSD kvm搞定
- 不需要在
initializationOptions
里指定resourceDir
了,感謝jiegec的#137 - 各種模板改進和function template/class template內函數引用的支持。支持了
CXCursor_OverloadedDeclRef
函數調用#174,但template call template clang-c介面沒有暴露相應信息,可能無解。 textDocument/hover
信息把函數名插入到函數類型里。用了一些heuristics處理_Atomic
decltype()
throw()__attribute(())
typeof()
,碰到-> int(*)()
這種還是沒救的,數組括弧也不好,但大多數情況都顯示得不錯的。- 加入了實驗性的
--enable-comments
,檢索注釋。#183 #188 #191 注釋和原來的聲明信息一起顯示,帶來了UI的挑戰。 - VSCode使用浮動窗口,顯示多行
textDocument/hover
不成問題。但Emacs lsp-mode和LanguageClient-neovim就遇到一些困難LanguageClient-neovim#224 lsp-ui#17 workspace/symbol
模糊匹配 #182- danielmartin自己的repo加了實驗性的
textDocument/formatting
,格式化。這必須用clang C++ API,作者有一些顧慮。
用了一個O(n^2) sequence alignment演算法,根據編輯距離、camelCase等啟發因素排序候選符號(func,type,path,…)。以foo bar
為模式會返回fooBar foobar foozbar
等,而fooBar
排在前面。Emacs xref-find-apropos
會自作聰明地把模式用空格分割後當作正規表達式轉義,需要覆蓋掉。
libclang handleReference
問題
下面介紹重點,libclang一位元組補丁。
#192
% cd /tmp% git clone https://github.com/nlohmann/json% cd json% (mkdir build; cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON ..)% ln -s build/compile_commands.json% nvim test/src/unit-constructor1.cpp # or other editor
cquery命令行選項加--log-file /tmp/cq.log
可以看到
indexer.cc:1892 WARN| Indexing /tmp/json/test/src/unit-iterators2.cpp failed with errno=1libclang: crash detected during indexing TU
errno=1
指CXError_Failure
,libclang使用crash recovery機制保護了SIGSEGV並返回CXError_Failure
。這類源文件沒有任何hover、definition、references信息。
原因是libclang/CXIndexDataConsumer.cpp#L203
handleReference(ND, Loc, Cursor, dyn_cast_or_null<NamedDecl>(ASTNode.Parent), ASTNode.ContainerDC, ASTNode.OrigE, Kind);
dyn_cast_or_null<NamedDecl>(ASTNode.Parent)
可能會NULL,在libclang/CXIndexDataConsumer.cpp#L935處
ContainerInfo Container; getContainerInfo(DC, Container);
getContainerInfo
會cast
DC
,null pointer dereference而SIGSEGV。
補丁已經提交到上游,但clang+llvm 5.0.1剛剛發布,等這個patch又要好久。對於無法/不願意編譯clang+llvm的Linux ./waf configure --bundled-clang=5.0.1
用戶。 我的修復方案是
# --bundled-clang=5.0.1% printf x4d | dd of=build/release/lib/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04/lib/libclang.so.5.0 obs=1 seek=$[0x47aece] conv=notrunc# --bundled-clang=4.0.0% printf x4d | dd of=build/release/lib/clang+llvm-4.0.0-x86_64-linux-gnu-ubuntu-14.04/lib/libclang.so.4.0 obs=1 seek=$[0x4172b5] conv=notrunc
What?
說明
bool CXIndexDataConsumer::handleReference(const NamedDecl *D, SourceLocation Loc, CXCursor Cursor, const NamedDecl *Parent, const DeclContext *DC, const Expr *E, CXIdxEntityRefKind Kind) { if (!CB.indexEntityReference) return false; if (!D) return false; if (Loc.isInvalid()) return false;
根據System V x86-64 ABI,這個成員函數的參數傳遞方式:
this: rdiD: rsiLoc: rdxCursor: stackParent: rcxDC: r8E: r9Kind: stack
cquery會設置CB.indexEntityReference
為OnIndexReference
,因此該值保證非NULL。
找到if (!CB.indexEntityReference)
這個檢查
% objdump -M intel -Cd build/release/lib/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04/lib/libclang.so.5 --start-address 0x47ae90 --stop-address 0x47b190...... 47aeb0: 45 31 ed xor r13d,r13d 47aeb3: 45 85 ff test r15d,r15d 47aeb6: 0f 84 19 03 00 00 je 47b1d5 47aebc: 48 85 ed test rbp,rbp 47aebf: 0f 84 10 03 00 00 je 47b1d5 47aec5: 49 8b 44 24 18 mov rax,QWORD PTR [r12+0x18] # load `IndexerCallbacks &CB`, which is actually a pointer 47aeca: 48 8b 40 38 mov rax,QWORD PTR [rax+0x38] # load CB.indexEntityReference 47aece: 48 85 c0 test rax,rax # a redundant check to see if it is null; we replace it with check of `DC` 47aed1: 0f 84 fe 02 00 00 je 47b1d5 <clang::cxindex::CXIndexDataConsumer::handleReference(clang::NamedDecl const*, clang::SourceLocation, CXCursor, clang::NamedDecl const*, clang::DeclContext const*, clang::Expr const*, CXIdxEntityRefKind)+0x345>
0x47aeca處的test rax,rax
就是這個冗餘if,我們把它換成if (!DC)
即test r8,r8
48 85 c0 test rax,rax4d 85 c0 test r8,r8
修改一個位元組即可,即前面介紹的`printf+dd`。使用radare2的話可以用`r2 -nwqc wx 4d @ 0x47aece build/release/lib/clang+llvm-5.0.1-x86_64-linux-gnu-ubuntu-14.04/lib/libclang.so.5.0`
radare2真是不成器,關鍵時候test r8,r8
彙編又掛了radare2#9071??
推薦閱讀:
※為什麼Apple的Clang生成的LLVM IR比開源的Clang生成的IR要讀者友好?
※LLVM每日談之九 談LLVM的學習
※在編譯C語言代碼時,Clang跟gcc編譯器哪一個編譯出來的程序運行更快?特別是在浮點運算方面。
※Clang Parser漫步(三)——declarator的解析
※[貝聊科技]如何將 iOS 項目的編譯速度提高5倍