linux編譯clang
A. linux記錄死機前的函數調用
線上環境進程崩潰,運維為了不背鍋,要求崩潰之後立馬將進程拉起。然而發現有個問題:一旦運維將進程拉起之後,之後使用崩潰的 core 文件來進行分析時,符號信息都丟失,看到的都是問號。
但是,如果崩潰之後未被拉起,可以正常的看到符號。
後來發現,賀悶是運維啟動進程的 shell 腳本,每次啟動之前,會將需要載入的部分業務相關的 so 文件,文件名字修改(名稱里加上了時間戳,類似 lib20200423002608_xxxx.so 這種)。名稱被修改之後,gdb 自然沒法載入載入這個 so 文件。
info shared
在 gdb 里使用 info shared,可以看到這個 so 文件無對應的地址,因為沒有對應的 so 文件被載入。線上環境的 gdb版本是 7.2,啟動時沒有與 so 文件不存在相關的提示。
當然這是後話。
那麼在奔潰時,如何將奔潰時的調用棧記錄到日誌里呢。
可以藉助 backtrace 相關的 3 個函數來實現。
#include <execinfo.h>
int backtrace(void *symaddr[], int size);
char **backtrace_symbols(void *const symaddr[], int size);
void backtrace_symbols_fd(void *const *buffer, int size, int fd);
參數和返回值說明:
backtrace 傳入一個數組 symaddr,用來保存符號的地址;size 為數組的大小。size 應該足夠大,不然會有部分符號丟失。返回值為實際保存的地址數量。
backtrace_symbols 用來根據符號的地址,得到對應的符號。size 為 backtrace 的返回值,表示腔耐實際需要處理的符號數量。
返回的是一個 malloc 得到的字元串數組的起始地址(C 語言中不太嚴謹的講,char* 就是字元串),所以最後需要調用者釋放內存。
#include <stdlib.h>
#include <string>
#include <execinfo.h>
#include <unistd.h>
void getCallStackInfo(std::string &stackInfo)
{
static const int size = 100; //符號數量,100足夠
int nptrs;
void *buffer[size];
char **syms;
nptrs = backtrace(buffer, size); //返回當前調伍拍春用棧實際的符號數量
syms = backtrace_symbols(buffer, nptrs);
if (syms == nullptr)
{
perror("backtrace_symbols");
exit(EXIT_FAILURE);
}
for(int i = 0; i < nptrs; ++i)
{
stackInfo.append(syms[i]);
stackInfo.append("\n");
}
free(syms);
}
void say(int &n)
{
static int call_count = 0;
++n;
++call_count;
printf("call count %d\n", call_count);
if(call_count == 6)
{
std::string stack_info;
getCallStackInfo(stack_info);
printf("%s\n", stack_info.c_str());
return;
}
say(n);
}
int main()
{
int n = 3;
say(n);
return 0;
}
編譯運行,clang++ main.cpp -rdynamic -o main.out && ./main.out
call count 1
call count 2
call count 3
call count 4
call count 5
call count 6
./main.out(_Z16getCallStackInfoRNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE+0x23) [0x400d53]
./main.out(_Z3sayRi+0x6a) [0x400e8a]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(_Z3sayRi+0xc1) [0x400ee1]
./main.out(main+0x1f) [0x400f0f]
/lib64/libc.so.6(__libc_start_main+0xf3) [0x7fe51b555873]
./main.out(_start+0x2e) [0x400c6e]
看到調用棧已經被記錄下來,當然符號都是 name mangling 之後的,使用 c++filt _Z3sayRi 可以看到原始名字。
回到記錄奔潰時的調用棧到日誌里的主題上。通常的奔潰都是由於內存問題,那麼可以捕獲 SIGSEGV 信號,在信號處理函數中將當前的調用棧記錄到日誌中就行。寫文件可能需要一個 sleep 延時等待日誌線程處理完畢。
void sig_log_stack_handler(int sig)
{
std::string stackInfo;
getCallStackInfo(stackInfo);
abort();
}
當然嚴格的來說,在信號處理器函數里處理 IO 是不符合標準的,會 UB.
注意編譯時一定要帶上 -rdynamic 選項才有用。如果使用的是 qt creator,這個 -rdynamic 參數時需要傳給鏈接器的,需要在 .pro 文件里加上,加到 QMAKE_CXXFLAGS 是沒得用的。
QMAKE_LFLAGS += -rdynamic
B. 目前主流的c語言編譯軟體是什麼
C語言相比其他很多新興的、復雜的語言,語法還是簡單一些,較好實現的。
所以在C語言幾十年的發展中出現了各式各樣的編譯器,還有一些容易被誤解為編譯器的IDE。
這里列舉幾個主流的:
GCC
毫無疑問,GCC幾乎是unix及linux系統中最通用的編譯器套件,幾乎所有的linux發行版都預裝了GCC作為C語言的默認編譯器。除了對C語言的支持,GCC還支持C++、Objective-C等多種語言。GCC早在1987就由Richard Stallman作為GNU計劃的一部分發布。
Clang
Clang是近幾年新興的C/C++以及Objective-C的編譯器,Apple是其主要投資者,其最初的開發者已加盟Apple。雖說是新興,但其對C/C++標準的支持不亞於GCC等老牌編譯器,並且外部介面和GCC完全兼容,並且因其模塊化、錯誤提示完善等優點已經越來越受到重視。一些如FreeBSD等項目已將clang作為默認編譯器。
其實Clang並不是一個完整的編譯器,而是作為同一批開發者開發的另一個備受關注的虛擬機(類似於JVM)的llvm的一個前端開發,只是負責將C語言源碼編譯為llvm IR的中間語言,再由llvm編譯為目標代碼,這樣做可以讓其可移植性更好。
Microsoft Visual C++
作為擁有可視化集成編程系統的編譯器,VC被很多使用Windows作為開發環境的初學者使用。詳見網路的介紹
http://ke..com/view/2070966.htm?fromtitle=vc&fromid=7792954&type=syn#viewPageContent
C. 如何設置來用clang/clang++替換Linux下的默認編譯器Gcc
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 100
D. 什麼是linux kernel有什麼作用
Linux內核(英語:Linux kernel)是一種開源的類Unix操作系統宏內核。
工作於平板電腦、智能手機及智能手錶的Android操作系統同樣通過Linux內核提供的服務完成自身功能。
一個計算機系統是一個硬體和軟體的共生體,它們互相依賴,不可分割。計算機的硬體,含有外圍設備、處理器、內存、硬碟和其他的電子設備組成計算機的發動機。但是沒有軟體來操作和控制它,自身是不能工作的。
完成這個控制工作的軟體就稱為操作系統,在Linux的術語中被稱為「內核」,也可以稱為「核心」。Linux內核的主要模塊(或組件)分以下幾個部分:存儲管理、CPU和進程管理、文件系統、設備管理和驅動、網路通信,以及系統的初始化(引導)、系統調用等。
整個Linux操作系統家族基於該內核部署在傳統計算機平台(如個人計算機和伺服器,以Linux發行版的形式)和各種嵌入式平台,如路由器、無線接入點、專用小交換機、機頂盒、FTA接收器、智能電視、數字視頻錄像機、網路附加存儲(NAS)等。
工作於平板電腦、智能手機及智能手錶的Android操作系統同樣通過Linux內核提供的服務完成自身功能。盡管於桌面電腦的佔用率較低,基於Linux的操作系統統治了幾乎從移動設備到主機的其他全部領域。截至2017年11月,世界前500台最強的超級計算機全部使用Linux。
(4)linux編譯clang擴展閱讀:
編程語言
Linux是用C語言中的GCC版(這種C語言有對標准C進行擴展)寫的,還有幾個用匯編語言(用的是GCC的"AT&T風格")寫的目標架構短段。因為要支持擴展的C語言,GCC在很長的時間里是唯一一個能正確編譯Linux的編譯器。
有許多其他的語言用在一些方面上,主要集中在內核構建過程中(這里指從源代碼創建可引導鏡像)。包括Perl、Python和多種腳本語言。有一些驅動可能是用C++、Fortran或其他語言寫的,但是這樣是強烈不建議的。
編譯器兼容性
GCC是Linux內核源代碼的預設編譯器。在2004年,Intel主張通過修改內核,以便Intel C++編譯器能正確編譯內核。在2009年,有通過修改內核2.6.22版而成功編譯的報告(並帶來平均8-9%性能增長)。
自從2010年,已經開始進行使用Clang建造Linux內核的努力,Clang是一個可作為替代的C語言編譯器;截止2014年4月12日,官方內核幾乎可以完全用Clang編譯。致力於這個目標的計劃叫做「LLVMLinux」,得名於Clang所基於的LLVM編譯器下部構造。
LLVMLinux不意圖復制Linux內核或LLVM,因此它是由最終提交給上游計劃的補丁構成的一個元計劃。使Linux內核可以用Clang編譯最大的好處是比GCC有更快的編譯速度,內核開發者可以得益於由此而來的更快的工作流程
E. 如何使用Ninja快速編譯LLVM和Clang
1,Build llvm/clang/lldb/仔笑lld 3.5.0等組件
1.0 准備:
至少需簡戚絕要從llvm.org下載llvm, cfe, lldb, compiler-rt,lld等3.5.0版本的代碼。
$tar xf llvm-3.5.0.src.tar.gz
$cd llvm-3.5.0.src
$mkdir -p tools/clang
$mkdir -p tools/clang/tools/extra
$mkdir -p tools/lld
$mkdir -p projects/compiler-rt
$tar xf cfe-3.5.0.src.tar.xz -C tools/clang --strip-components=1
$tar xf compiler-rt-3.5.0.src.tar.xz -C projects/compiler-rt --strip-components=1
$tar xf lldb-3.5.0.src.tar.xz -C tools/clang/tools/extra --strip-components=1
$tar xf lld-3.5.0.src.tar.xz -C tools/lld --strip-components=1
1.1 【可選】使用clang --stdlib=libc++時,自動添加-lc++abi。
libc++組件可以使用gcc libstdc++的supc++ ABI,也可以使用c++abi,cxxrt等,實際上自動添加-lc++abi是不必要的,這里這么處理,主要是為了方便起見。實際上完全可以在「clang++ -stdlib=libc++」時再手工添加-lc++abi給鏈接器。
這里涉及到鏈接時DSO隱式還是顯式的問題,早些時候ld在鏈接庫時會自動引入由庫引入的依賴動態庫,後來因為這個行為的不可控性,所以ld鏈接器的行為做了修改,需要顯式的寫明所有需要鏈接的動態庫,才會有手工添加-lc++abi這種情況出現。
攔姿--- llvm-3.0.src/tools/clang/lib/Driver/ToolChain.cpp 2012-03-26 18:49:06.663029075 +0800
+++ llvm-3.0.srcn/tools/clang/lib/Driver/ToolChain.cpp 2012-03-26 19:36:04.260071355 +0800
@@ -251,6 +251,7 @@
switch (Type) {
case ToolChain::CST_Libcxx:
CmdArgs.push_back("-lc++");
+ CmdArgs.push_back("-lc++abi");
break;
case ToolChain::CST_Libstdcxx:
1.2 【必要】給clang++添加-fnolibgcc開關。
這個開關主要用來控制是否連接到libgcc或者libunwind。
註:libgcc不等於libunwind。libgcc_eh以及supc++的一部分跟libunwind功能相當。
註:libgcc_s和compiler_rt的一部分相當。
這個補丁是必要的, 不會對clang的正常使用造成任何影響 ,只有在使用「-fnolibgcc"參數時才會起作用。
之所以進行了很多unwind的引入,主要是為了避免不必要的符號缺失麻煩,這里的處理相對來說是干凈的,通過as-needed規避了不必要的引入。
--- llvm-static-3.5.0.bak/tools/clang/lib/Driver/Tools.cpp 2014-09-10 13:46:02.581543888 +0800
+++ llvm-static-3.5.0/tools/clang/lib/Driver/Tools.cpp 2014-09-10 16:03:37.559019321 +0800
@@ -2060,9 +2060,15 @@
".a");
CmdArgs.push_back(Args.MakeArgString(LibClangRT));
- CmdArgs.push_back("-lgcc_s");
- if (TC.getDriver().CCCIsCXX())
- CmdArgs.push_back("-lgcc_eh");
+ if (Args.hasArg(options::OPT_fnolibgcc)) {
+ CmdArgs.push_back("--as-needed");
+ CmdArgs.push_back("-lunwind");
+ CmdArgs.push_back("--no-as-needed");
+ } else {
+ CmdArgs.push_back("-lgcc_s");
+ if (TC.getDriver().CCCIsCXX())
+ CmdArgs.push_back("-lgcc_eh");
+ }
}
static void addProfileRT(
@@ -7150,24 +7156,50 @@
bool isAndroid = Triple.getEnvironment() == llvm::Triple::Android;
bool StaticLibgcc = Args.hasArg(options::OPT_static_libgcc) ||
Args.hasArg(options::OPT_static);
+
+
+
if (!D.CCCIsCXX())
- CmdArgs.push_back("-lgcc");
+ if (Args.hasArg(options::OPT_fnolibgcc)) {
+ CmdArgs.push_back("--as-needed");
+ CmdArgs.push_back("-lunwind");
+ CmdArgs.push_back("--no-as-needed");
+ } else
+ CmdArgs.push_back("-lgcc");
if (StaticLibgcc || isAndroid) {
if (D.CCCIsCXX())
- CmdArgs.push_back("-lgcc");
+ if (Args.hasArg(options::OPT_fnolibgcc)) {
+ CmdArgs.push_back("--as-needed");
+ CmdArgs.push_back("-lunwind");
+ CmdArgs.push_back("--no-as-needed");
+ } else
+ CmdArgs.push_back("-lgcc");
} else {
if (!D.CCCIsCXX())
CmdArgs.push_back("--as-needed");
- CmdArgs.push_back("-lgcc_s");
+ if (Args.hasArg(options::OPT_fnolibgcc))
+ CmdArgs.push_back("-lunwind");
+ else
+ CmdArgs.push_back("-lgcc_s");
if (!D.CCCIsCXX())
CmdArgs.push_back("--no-as-needed");
}
if (StaticLibgcc && !isAndroid)
- CmdArgs.push_back("-lgcc_eh");
+ if (Args.hasArg(options::OPT_fnolibgcc)) {
+ CmdArgs.push_back("--as-needed");
+ CmdArgs.push_back("-lunwind");
+ CmdArgs.push_back("--no-as-needed");
+ } else
+ CmdArgs.push_back("-lgcc_eh");
else if (!Args.hasArg(options::OPT_shared) && D.CCCIsCXX())
- CmdArgs.push_back("-lgcc");
+ if (Args.hasArg(options::OPT_fnolibgcc)) {
+ CmdArgs.push_back("--as-needed");
+ CmdArgs.push_back("-lunwind");
+ CmdArgs.push_back("--no-as-needed");
+ } else
+ CmdArgs.push_back("-lgcc");
// According to Android ABI, we have to link with libdl if we are
// linking with non-static libgcc.
--- llvm-static-3.5.0.bak/tools/clang/include/clang/Driver/Options.td 2014-08-07 12:51:51.000000000 +0800
+++ llvm-static-3.5.0/tools/clang/include/clang/Driver/Options.td 2014-09-10 13:36:34.598511176 +0800
@@ -788,6 +788,7 @@
def fomit_frame_pointer : Flag<["-"], "fomit-frame-pointer">, Group<f_Group>;
def fopenmp : Flag<["-"], "fopenmp">, Group<f_Group>, Flags<[CC1Option, NoArgumentUnused]>;
def fopenmp_EQ : Joined<["-"], "fopenmp=">, Group<f_Group>, Flags<[CC1Option]>;
+def fnolibgcc : Flag<["-"], "fnolibgcc">, Group<f_Group>, Flags<[CC1Option, NoArgumentUnused]>;
def fno_optimize_sibling_calls : Flag<["-"], "fno-optimize-sibling-calls">, Group<f_Group>;
def foptimize_sibling_calls : Flag<["-"], "foptimize-sibling-calls">, Group<f_Group>;
def force__cpusubtype__ALL : Flag<["-"], "force_cpusubtype_ALL">;
1.3 llvm的其他補丁。
llvm/clang將gcc toolchain的路徑hard code在代碼中,請查閱tools/clang/lib/Driver/ToolChains.cpp。
找到x86_64-redhat-linux之類的字元串。
如果沒有你系統特有的gcc tripple string,請自行添加。
這個tripple string主要是給llvm/clang搜索gcc頭文件等使用的,不影響本文要構建的toolchain
1.4 構建clang/llvm/lldb
本文使用ninja。順便說一下,llvm支持configure和cmake兩種構建方式。可能是因為工程太大,這兩種構建方式的工程文件都有各種缺陷(主要表現在開關選項上,比如configure有,但是cmake卻沒有等)。llvm-3.4.1就是因為cmake工程文件的錯誤而導致了3.4.2版本的發布。
綜合而言,cmake+ninja的方式是目前最快的構建方式之一,可以將構建時間縮短一半以上。
mkdir build
cd build
cmake \
-G Ninja \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_BUILD_TYPE="Release" \
-DCMAKE_CXX_FLAGS="-std=c++11" \
-DBUILD_SHARED_LIBS=OFF \
-DLLVM_ENABLE_PIC=ON \
-DLLVM_TARGETS_TO_BUILD="all" \
-DCLANG_VENDOR="MyOS" ..
ninja
ninja install
如果系統原來就有clang/clang++的可用版本,可以添加:
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
這樣就會使用系統的clang++來構建llvm/clang
2,測試clang/clang++。
自己找幾個簡單的c/cpp/objc等編譯測試一下即可。完整測試可以在構建時作ninja check-all
3,libunwind/libc++/libc++abi,一套不依賴libgcc, libstdc++的c++運行庫。
3.1 從https://github.com/pathscale/libunwind 獲取代碼。
libunwind有很多個實現,比如gnu的libunwind, path64的libunwind,還有libcxxabi自帶的Unwinder.
這里作下說明:
1),gnu的libunwind會有符號缺失和沖突。
2),libcxxabi自帶的Unwinder是給mac和ios用的,也就是只能在darwin體系構建。目前Linux的實現仍然不全,等linux實現完整了或許就不再需要path64的unwind實現了。
暫時建議使用pathscale的unwind實現。
mkdir -p build
cd build
cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_C_FLAGS="-m64" ..
ninja
mkdir -p /usr/lib
cp src/libunwind.so /usr/lib
cp src/libunwind.a /usr/lib
3.2 第一次構建libcxx.
必須先構建一次libcxx,以便後面構建libcxxabi。這里構建的libcxx實際上是使用gcc的libgcc/stdc++/supc++的。
打上這個補丁來禁止libgcc的引入:
diff -Nur libcxx/cmake/config-ix.cmake libcxxn/cmake/config-ix.cmake
--- libcxx/cmake/config-ix.cmake 2014-06-25 06:57:50.000000000 +0800
+++ libcxxn/cmake/config-ix.cmake 2014-06-25 09:05:24.980350544 +0800
@@ -28,5 +28,4 @@
check_library_exists(c printf "" LIBCXX_HAS_C_LIB)
check_library_exists(m ccos "" LIBCXX_HAS_M_LIB)
check_library_exists(rt clock_gettime "" LIBCXX_HAS_RT_LIB)
-check_library_exists(gcc_s __gcc_personality_v0 "" LIBCXX_HAS_GCC_S_LIB)
編譯安裝:
mkdir build
cd build
cmake \
-G Ninja \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_C_COMPILER=clang \
-DCMAKE_CXX_COMPILER=clang++ \
..
ninja
ninja install
3.3,測試第一次構建的libcxx。
使用"clang++ -stdlib=libc++ -o test test.cpp -lstdc++"編譯簡單c++代碼,檢查是否出錯。(如果前面構建clang是已經apply了c++abi的鏈接補丁,這里會出現找不到c++abi的情況,跳過即可)
使用"ldd test"查看test二進制動態庫使用情況。可以發現,test依賴於libgcc_s/libc++/libstdc++。(多少有些不爽了吧?使用了libc++居然還要依賴libstdc++?)
F. linux下Clang和gcc的區別
是兩種不同的C++編譯器。gcc歷史很悠久了,而clang是新興的編譯器,已皮高經兼容gcc,也全燃穗尺面支持C++11標准族團、Objective-C等,當然二者都是cross-platform的。具體的區別可以移步維基網路中gcc和clang詞條。
G. 如何設置來用clang/clang++替換Linux下的默認編譯器Gcc
我暈, 你到底是在什麼發行版? 從提示上來看,要用 apt-get install 來裝,說明是 ubuntu/debian之類的linux發行版,你怎麼又會去用 rpm 來查詢和安裝軟體? 你不說你是什麼發行版,樓上回答的人也不管,直接就讓你用rpm,誤人子弟啊。
正確的方法是,
sudo apt-get install build-essential
這個才是你的系統應該用的,裝好後命令行下運行
gcc -v
就會列印出你使用gcc的版本信息了,然後就可以用了,比如
gcc -o test test.c
就會編譯test.c,生成可執行文件 test
然後
./test
就會運行test
我再暈,樓主,提示你沒有test.c,你的C源文件呢?我這里是用test.c做例子,你的源文件叫什麼名字,你就把test.c換成你的文件的名字啊。另外,你要把你的源文件先保存在linux機器上,比方說放到了 /home/yourname/aaa
那你要先
cd /home/yourname/aaa
然後再
gcc -o test test.c
H. Linux下編寫c語言頭文件,編譯時出現「函數未定義」
出現該錯誤的原因為編譯時只變編譯了源文件的一部分,故在鏈接時無法從編譯好的目標代碼中找到print函數,所以出錯。
根據你的源文件結構,個人推薦先單獨編譯源文件為多個object文件,再統一鏈接。
方法如下
gcc-cmain.c#-c參數的作用是讓gcc只編譯,不鏈接
gcc-ctest.c
gcc-omainmain.otest.o#將編譯好的目標代碼鏈接
當然,以上是在gcc為編譯器的前提下執行,如使用clang等其他編譯器,方法類似(clang的調用方法與gcc高度兼容)
I. 如何使用clang+arm-linux-gcc編譯ARM程序並在模擬器上運
完全可以的,有arm-linux-gcc,你可以自己去編譯,這個需要的耐心很大,而且需要選擇好各種所需庫的版本,否則會出現編譯失敗;你也可以直接下載網上現成的.建議使用crosstool-ng集成編譯環境去編譯.
J. linux系統下能用vs2008編譯程序嗎如果不能的話一般用什麼編譯器
1、不可以,VS2008編譯的結果只能運行於WINDOWS
2 \linuX環境常用編譯開發環境:GNU C++,QT
3\更多交流參考我空間文章。