kk Blog —— 通用基础

date [-d @int|str] [+%s|"+%F %T"]

__builtin_return_address获得程序运行栈

gcc的编译特性使用__builtin_return_address(level)打印出一个函数的堆栈地址。其中 level代表是堆栈中第几层调用地址,__builtin_return_address(0)表示第一层调用地址,即当前函数,__builtin_return_address(1)表示第二层。如代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h>

void f()
{
	printf("%p,%p" , __builtin_return_address(0), __builtin_return_address(1));
	//printk("Caller is %pS\n", __builtin_return_address(0));
}

void g()
{
	f();
}
int main()
{
	g();
}

分别打印出函数f()和g() 的函数地址,我们通过objdump 出来的文件去查找打印出来的函数地址,这样就能看到调用的函数名了。

编译期间求值

编译期求阶乘

c++ 中的模板可以用来计算一些值,在编译的时候就是实现计算,而不是运行的时候。

求阶乘 n!,一般 me 们会写个这样的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
long Factorial(long n)
{
	return n == 0 ? 1 : n*Factorial(n-1);
}

int main()
{
	long fac=1, n=20;
	for(int i=1; i<=n; ++i)fac *= i;
	std::cout << "20! = " << fac << " " << Factorial(20) << std::endl;
	return 0;
}

现在使用模板技术,类似于递归的方法求 20 !。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>

template<int N>
class Factorial{
public:
	static const long value = N*Factorial<N-1>::value;
};

template<>
class Factorial<0>{
public:
	static const long value = 1;
};

int main()
{
	std::cout << "20! = " << Factorial<20>::value << std::endl;
	return 0;
}

说明:
template 通常用来参数化类型,通常 class T 或是 typename T(T 用来代替一个类型的名字),不过也可以带一个整型参数 N (貌似规定只能是整型)。
template <> 是用来特殊指定一些情形,比如上面给的 Factorial<0> 指定 N = 0 时的情形,这有点像递归中的 if(n==0) return 1;
class 类中可以带有 static const 变量,这种变量可以在类内初始化(只能是整型);当然既是 const 变量,又是 static 变量;
Factorila<20> 实际是一个类,而 ::value 是其 static 变量;在生成Factorila<20> 的时候同时生成了众多的Factorila ( N >0 && N < 20)类;

更多例子
模板类,或是模版函数,或是模板成员函数,都是编译器根据程序的实际情况而生成的,需要什么就生成什么,不需要就不生成。上面的例子中, 程序中使用 Factorial<20> 这个类,就生成这个类,因为 Factorial<20> 依赖 Factorial<19> 所以又生成 Factorial<19> ,这样一直依赖下去,直到 Factorial<0>( me 们已经指定了)。因为是编译期生成,也是编译器求值,所以实际程序中只能使用 static const 类似的 —— 常量,而不能使用普通的 int n。所以,模板元编程中,么发使用循环,只能类似递归的技术。
通常 me 们会将递归程序转换为循环程序,实际上循环程序基本也都可以递归解决。(是不是一定呢?O__O"…)
求斐波那契数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>

template <long N>
struct Fibonacci{
	static const long value = Fibonacci<N-1>::value + Fibonacci<N-2>::value;
};

template<>
struct Fibonacci<0>{
	static const long value = 0;
};

template<>
struct Fibonacci<1>{
	static const long value = 1;
};

int main()
{
	std::cout << Fibonacci<12>::value << std::endl;
	return 0;
}

第 12 个斐波那契数是 144,这是唯一一个 Fib(n) = n*n 的数。 求 1+2+3+…+n

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

template <long N>
struct Sum{
	static const long value = N+Sum<N-1>::value;
};

template<>
struct Sum<1>{
	static const long value = 1;
};

int main()
{
	std::cout << Sum<100>::value << std::endl;
	return 0;
}

这个和 n! 的用法基本一样。

constexpr编译期求值

模板只是在编译的时候完成工作的一种方法,实际上上面的模板元编程也只是在编译期求了一些常量而已;为了简化使用模板进行元编程的难度,c++11 引入了 constexpr 关键字 —— 声明常量或是函数,实现在编译期求值。上面的三个程序都可以大大简化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>

constexpr long factorial(long n)
{
	return n<=1 ? 1 : n*factorial(n-1);
}
constexpr long fibonacci(long n)
{
	return n<=1 ? n : fibonacci(n-1)+fibonacci(n-2);
}
constexpr long sum(long n)
{
	return n<=1 ? n : n+sum(n-1);
}

int main()
{
	std::cout << "10! F(12) 1+2+...+100 => " << factorial(10) << " " << fibonacci(12) << " " << sum(100) << std::endl;
	return 0;
}

不用多数,看应该看得懂神马意思,要提的就是 constexpr 都是编译的时候求值。

binutils(含as、ld等)静态编译

binutils下载 http://ftp.gnu.org/gnu/binutils/

binutils静态编译:

1
2
./configure
make LDFLAGS=-all-static
原因:

他们链接的时候是通过 ./libtool 完成的,在libtool里有一行提示(./libtool –help没有显示这个提示):

1
-all-static       do not do any dynamic linking at all

所以就是要libtool增加-all-static参数

比较通用的静态编译方法

1
2
3
4
5
./configure 后加   CFLAGS=-static --enable-static LDFLAGS=-static --disable-shared
./configure 后加   CFLAGS=-static LDFLAGS=-static
make CFLAGS=-static LDFLAGS=-static

systemtap article

https://sourceware.org/systemtap/wiki/WarStories

http://wenku.baidu.com/view/9045426048d7c1c708a1452d.html

http://www.cnblogs.com/hazir/p/systemtap_introduction.html

https://sourceware.org/systemtap/wiki/WarStories

http://www.cnblogs.com/wangkangluo1/archive/2012/06/26/2562971.html

http://www.ibm.com/developerworks/cn/linux/l-systemtap/index.html

http://blog.yufeng.info/archives/855

http://www.360doc.com/content/12/0523/15/7982302_213133871.shtml

https://sourceware.org/systemtap/wiki/AddingUserSpaceProbingToApps

http://www.docin.com/p-610001474.html

http://blog.chinaunix.net/uid-7585066-id-2048719.html

http://blog.chinaunix.net/uid-20568790-id-1632313.html

wiki翻译

http://blog.csdn.net/linyt/article/details/5204841

gcc编译安装

ftp://ftp.gnu.org/pub/gnu/gcc/gcc-4.6.2/gcc-4.6.2.tar.gz

ftp://gcc.gnu.org/pub/gcc/infrastructure/gmp-4.3.2.tar.bz2

ftp://gcc.gnu.org/pub/gcc/infrastructure/mpfr-2.4.2.tar.bz2

ftp://gcc.gnu.org/pub/gcc/infrastructure/mpc-0.8.1.tar.gz

安装依赖

1
gcc configure: error: Building GCC requires GMP 4.2+, MPFR 2.3.1+ and MPC 0.8.0+

从错误中可以看出:GCC编译需要GMP, MPFR, MPC这三个库(有的系统已经安装了就没有这个提示,我的没有安装),有两种安装方法(建议第二种):

手动安装

我使用的版本为gmp-4.3.2,mpfr-2.4.2和mpc-0.8.1,在 ftp://gcc.gnu.org/pub/gcc/infrastructure/ 下载,根据提示的顺序分别安装GMP,MPFR和MPC(mpfr依赖gmp,mpc依赖gmp和mpfr),这里全部自己指定了安装目录,如果没有指定则默认分装在在/usr/include、/usr/lib和/usr/share,管理起来不方便,比如想卸载的时候还得一个个去找:

1
2
3
安装gmp:  ./configure --prefix=/usr/local/gmp-4.3.2; make install
安装mpfr: ./configure --prefix=/usr/local/mpfr-2.4.2 --with-gmp=/usr/local/gmp-4.3.2/; make install
安装mpc:  ./configure --prefix=/usr/local/mpc-0.8.1 --with-gmp=/usr/local/gmp-4.3.2/ --with-mpfr=/usr/local/mpfr-2.4.2/; make install

gcc自带脚本安装

gcc源码包中自带了一个gcc依赖库安装脚本download_prerequisites,位置在gcc源码目录中的contrib/download_prerequisites,因此只需要进入该目录,直接运行脚本安装即可:./download_prerequisites

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MPFR=mpfr-2.4.2
GMP=gmp-4.3.2
MPC=mpc-0.8.1

wget ftp://gcc.gnu.org/pub/gcc/infrastructure/$MPFR.tar.bz2 || exit 1
tar xjf $MPFR.tar.bz2 || exit 1
ln -sf $MPFR mpfr || exit 1

wget ftp://gcc.gnu.org/pub/gcc/infrastructure/$GMP.tar.bz2 || exit 1
tar xjf $GMP.tar.bz2  || exit 1
ln -sf $GMP gmp || exit 1

wget ftp://gcc.gnu.org/pub/gcc/infrastructure/$MPC.tar.gz || exit 1
tar xzf $MPC.tar.gz || exit 1
ln -sf $MPC mpc || exit 1

rm $MPFR.tar.bz2 $GMP.tar.bz2 $MPC.tar.gz || exit 1

配置环境变量

我这里指定了安装位置,如果没有指定则这几个库的默认位置是/usr/local/include和/usr/local/lib,不管有没有指定GCC编译时都可能会找不到这三个库,需要确认库位置是否在环境变量LD_LIBRARY_PATH中,查看环境变量内容可以用命令

1
echo $LD_LIBRARY_PATH

设置该环境变量命令如下:

1
2
3
指定安装:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/gmp-4.3.2/lib:/usr/local/mpfr-2.4.2/lib:/usr/local/mpc-0.8.1/lib

默认安装:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib

不指定环境变量会出错:

1
configure: error: cannot compute suffix of object files: cannot compile

2.编译gcc

1)建立一个objdir来存放目标文件 然后进入该文件夹输入
1
2
/home/wulei/sourcecode/gcc-4.6.2/configure --prefix=/usr/local/gcc-4.6.2 --enable-threads=posix --disable-checking --disable-multilib --enable-languages=c --with-gmp=/usr/local/gmp-4.3.2/ --with-mpfr=/usr/local/mpfr-2.4.2/ --with-mpc=/usr/local/mpc-0.8.1/
最终用:../gcc-4.6.2/configure --prefix=/usr/gcc-4.6.9 --enable-threads=posix --disable-checking --disable-multilib --enable-languages=c --with-gmp=/usr/gmp-4.3.2 --with-mpfr=/usr/mpfr-2.4.2 --with-mpc=/usr/mpc-0.8.1
2)
1
2
3
make
make check
make install
错误1
1
2
/usr/bin/ld: .libs/expat_justparse_interface.o: relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with -fPIC
.libs/expat_justparse_interface.o: could not read symbols: Bad value

解决

1
make CXXFLAGS=-fPIC CFLAGS=-fPIC
出现问题make的时候提示如下:
1
2
3
4
5
6
7
8
Checking for suffix of object files... configure: error: in `/home/wulei/sourcecode/gcc-4.6.2/i686-pc-linux-gnu/libgcc':
configure: error: cannot compute suffix of object files: cannot compile
See `config.log' for more details.
make[2]: *** [configure-stage1-target-libgcc] 错误 1
make[2]:正在离开目录 `/home/wulei/sourcecode/gcc-4.6.2'
make[1]: *** [stage1-bubble] 错误 2
make[1]:正在离开目录 `/home/wulei/sourcecode/gcc-4.6.2'
make: *** [all] 错误 2

于是 进入/home/wulei/sourcecode/gcc-4.6.2/i686-pc-linux-gnu/libgcc查看这个路径下的config.log
发现如下的错误提示:

1
/home/wulei/sourcecode/gcc-4.6.2/host-i686-pc-linux-gnu/gcc/cc1: error while loading shared libraries: libmpfr.so.1: cannot open shared object file: No such file or directory

原因是因为linux在make的时候没有自动寻找新加入的库所以要用命令加入

1
export LD_LIBRARY_PATH=/usr/local/mpc-0.8.1/lib:/usr/local/mpfr-2.4.2/lib:/usr/local/gmp-4.3.2/lib

Makefile:161: ../.././gcc/libgcc.mvars: No such file or directory

编译gcc时,需要注意一个原则:不要再gcc的源码中直接执行./configure、make、make install等命令,需要在源码目录下另外新建一个目录,在新建的目录中执行以上命令。