KGDB && Build Ubuntu kernel

关于KGDB:GDB是GUN的调试器,用来调试普通程序。KGDB是用来调试操作系统内核的。当我还在念小学的时候,Linus严辞拒绝KGDB进入主线内核的,理由是:真正懂内核的人不需要工具来调试,人脑就能调代码[1]。可是到了2.6.26时代,却已经欣然接受KGDB,很想知道是谁说服了那个顽固的天才。

下面开始在Ubuntu 10.04下快乐的使用KGDB。

为什么是Ubuntu 10.04?我用过的发行版不多,也就Ubuntu、Archlinux、Redhat、Centos、Fedora、LFS。Ubuntu并不是最好用的,很多地方都不符合我的习惯,但是,因为手边正好有一张Ubuntu的推广光盘,这张质量上乘的推广光盘使我把附近能用的机器都装上了Ubuntu。光盘安装的便利性是各种硬盘安装、网络安装、U盘安装等繁琐手段无法比拟的。于是乎,我被Ubuntu俘虏了。

真要我说Ubuntu最吸引我的地方,大概就是它那个默认的红色配色了,看着很舒服。字体也刚刚好。

Ubuntu 不太适合搞KGDB,因为在Ubuntu上换内核特别麻烦。注意,是特别麻烦。

10.04的内核默认已经打开了KGDB选项,但是因为发行版不提供原始的vmlinux文件,因此需要重做一个(反正已经重编了内核,顺手就换下内核,好处是可以完美match 符号表,自己动手编的vmlinux文件和官方的核不能100%契合)。

官方的wiki上提供了两种编译内核的方式,一种是编Ubuntu kernel一种是编主线kernel

图省事我编的是Ubuntu kernel。

首先拿到内核源码,推荐使用git方式。

Git路径如下:

quantal

git://kernel.ubuntu.com/ubuntu/ubuntu-quantal.git

precise

git://kernel.ubuntu.com/ubuntu/ubuntu-precise.git

oneiric

git://kernel.ubuntu.com/ubuntu/ubuntu-oneiric.git

natty

git://kernel.ubuntu.com/ubuntu/ubuntu-natty.git

lucid

git://kernel.ubuntu.com/ubuntu/ubuntu-lucid.git

hardy

git://kernel.ubuntu.com/ubuntu/ubuntu-hardy.git

10.04 是lucid,所以执行

$git clone git://kernel.ubuntu.com/ubuntu/ubuntu-lucid.git

然后切换至对应的tag上。查看版本

$uname -a

Linux hacklu-desktop 2.6.32-21-generic #32-Ubuntu SMP

所以对应的tag是Ubuntu-2.6.32-21.32。

$git checkout -b my_kernel Ubuntu-2.6.32-21.32

编译选项最好在发行版的基础上修改

$cp /boot/config-2.6.32-21-generic .config

$make oldconfig

在这基础上修改自定义的配置(调试内核,需要把CONFIG_DEBUG_RODATA关闭,代码段的只读属性不去掉,KGDB无法下断点。因为断点是通过把欲断处指令改成INT3来实现)

$make menuconfig

$make-kpkg clean # only needed if you want to do a "clean" build

$cp Ubuntu-kernel-src/debian/control-scripts/{postinst,postrm,preinst,prerm} ~/kernel-package/pkg/image/

$ cp Ubuntu-kernel-src /debian/control-scripts/headers-postinst ~/kernel-package/pkg/headers/

$CONCURRENCY_LEVEL=2  fakeroot make-kpkg --initrd --append-to-version=-hacklu.v1 --overlay-dir=~/kernel-package/  kernel_image kernel_headers

 

解释下, CONCURRENCY_LEVEL=2 相当于平时的make -j2 编译速度加快。

fakeroot 是make-kpkg以为你是root

--append-to-version=-hacklu.v1 后面的字符串会自动加在编译好的内核版本后面

--overlay-dir=~/kernel-package/  make-kpkg 命令需要这个选项指定定制脚本路径,如果没有这一项,编译出来的内核缺少initramfs。

Kernel_image 表示编译image.deb

Kernel_header表示编译header.deb

命令执行完毕会在当前目录的上一级生成两个deb包,分别安装即可

$sudo dpkg -i *.deb

Image.deb会根据控制脚本自动生成initramfs,并更新grub,重启即可切换至新编出的内核。

 

此刻KGDB 和vmlinux都已经准备好了。如果有串口线则可调试实体机器,不然就放在vmware中调试。

Vmware中安装好一台ubuntu10.04的系统做被调试机,然后add device—》serial port—》named pipe

如下,记得要勾上Yield CPU on poll。

如果调试机也是虚拟机,类似添加串口设备,server和client互换。

如果调试机是局域网内一台物理机器,则可以利用命名管道转网络工具[2]把虚拟机中的被调试机器的串口转发到局域网中去。

在被调试机器上执行如下指令

$sudo -s

#echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc   #指定通信串口号

#echo g > /proc/sysrq-trigger  #挂起被调试机器,产生kgdb_breakpoint中断。此时调试端gdb可以连入

在调试机器上执行如下

$gdb vmlinux

$target remote /dev/ttyS0

之后就可以开心的使用GDB调试内核了。

Ps:最好使用Ubuntu自带的gdb来调试,10.04的内核跟7.2以上的GDB不合拍。

 

 

[1] Linus say: Without a debugger, you basically have
to go the next step: understand what the program does. Not just that
particular line. https://lkml.org/lkml/2000/9/6/65
[2] Named Pipe TCP Proxy Utility http://shvechkov.tripod.com/nptp.html

LFS && Raspberry && pin

最近干了这三件事。记录之。

LFS:Linux from scratch。

LFS是我的一个心结,很久之前就说要好好做一遍这个,却一直借口没有时间,其实真正做下来也就花了25个小时左右,两次机房血战就差不多能做一次了。

LFS的基本思想是借助一台已有的linux环境,从碎片开始安装linux。自己搭建工具链,自己编译内核,自己编译shell。所有的一切都是从源代码开始一点一点慢慢编译。

做LFS学到的东西比我预期少了点,init之后的启动流程还是云里雾里,估计这阶段是不会认真去读那堆启动脚本了,留个遗憾。

最大的收获就是工具链的制作原理以及常用命令都是来自哪个包。

GNU的工具链分为三个部分,binutil,Glibc,GCC。

GCC负责编译,把c语言代码编译成汇编语言。这期间会用到C库(glibc)。

Glibc主要包括C库,还有动态加载器(/lib/ld-linux.so.2),此动态加载器负责加载二进制文件到进程地址空间。

Binutil是二进制工具,处理二进制文件。包括ar,as(汇编器,把汇编文件翻译成二进制文件),ld(静态链接器),以及我最喜欢的反编译objdump。

这三者的关系如下:

Gcc编译过程需要链接Glibc。

Binutil编译过程需要链接Glibc。

Glibc是自包含的,编译过程不需要链接到Glibc,但是它需要工具链来编译自己,并受到工具链的影响,工具链是什么?工具链就是gcc+glibc+binutil(严格来讲编译glibc时不需要宿主的glibc,只要gcc和binutil就可以)。

基本上算是循环依赖= =!。

那么如何制作一个pure的工具链?LFS曾今的做法也是有缺陷的,后来被一名内核黑客提出了改进办法[1]。

现今的办法如下:

(注:gcc0表示第0代gcc,也就是宿主上原来的gcc程序,gcc1表示第一次编译出来的gcc。其他同)

gcc0 + glibc0 + binutil0 -> binutil1,gcc1 (一代gcc和binutil链接都是宿主上的glibc,所以受宿主的影响)

gcc1 + binutil1 -> glibc1 (一代glibc 只受一代gcc影响,开始隔离宿主)

gcc1 + glibc1 + binutil1 -> binutil2, gcc2 (二代工具链用的是新生成的glibc1)

gcc2,binutil2,glibc1 三者构成了临时编译环境。用这套工具链生成了一堆临时环境需要用到的各种工具,比如make,sed等等。

Gcc2 + binutil2 -> glibc2

Gcc2 + binutil2 + glibc2 -> gcc3 ,binutil3

Gcc3,binutil3,glibc2 三者构成了最终LFS的编译环境,LFS上最后运行的所有程序都是来自这三个家伙。

(吐槽一句,其实无论怎么折腾,这也是个鸡生蛋蛋生鸡的问题,只要在gcc0中做手脚,后面的gcc永远不会干净。在此向伟大的C语言之父Dennis Ritchie致敬。)

LFS意外收获:

  1. cp abc{,.new}  = cp abc abc.new
  2. tar xf file.tar.**          不用指定压缩算法,tar自动探测,指定还容易指定错- -!
  3. W命令看当前登录用户,lastb 看最后失败登录。

 

 

[1]http://www.linuxfromscratch.org/hints/downloads/files/OLD/pure_lfs.txt

《深入探索C++对象模型》读书笔记


从C语言发展而来的C++增加了类的概念以及其带来的面向对象编程思维。OO四大特性我认为最重要的是多态性,因为多态势必牵涉到继承,要继承必须先抽象和封装好基类。

而编译器为了支持多态,在程序员书写的源代码背后做了不少手脚。多态的核心在于延迟绑定,即执行的时候才知道该调用哪个函数(C++为了效率,编译时就已确定函数指针)。C++中为了实现延迟绑定,多了一层间接性,即引入了Virtual table和Virtual table pointer(分别简记为vtbl,vptr)。正是因为多了上述的vptr使得upcasting到基类的指针或者引用可以动态的调用子类的函数体。

C++的处事之道在于:如果不是必需的额外负担,一概以效率为先。因此只有涉及到virtual关键字(虚成员函数,虚继承)的类才会有vtbl。当然,也只有使用了virtual关键字才能享受OO多态带来的一系列好处。

Vtbl和vptr对程序员是不可见的,由编译器全权负责。在对象创建的时候(默认构造函数,拷贝构造函数),以及拷贝赋值的时候(copy-assignment operation)编译器必需生成适当的代码来正确设置vptr。

当程序员使用多态机制调用函数时,编译器必需用vptr来改写程序员的代码,使之通过vptr来间接调用“正确的”成员函数。当涉及多继承和虚继承时,还有复杂的this指针调整。

下面通过实例来看。

首先是未使用virtual关键字的普通类,此时的函数调用和C类似,直接绑定函数地址。此时编译器不会生成vtbl和vptr,所以没有任何多态特性。

class  c_style

{

public:

int c;

void who_am_I ()

{

cout<<“c style \n”;

}

class cc_style:c_style

{

void who_am_I ( )

{

cout<<“cc style\n”;

}

}

int main()

{

c_style c,*pc;

cc_style cc;

pc=&cc;

c.who_am_I();            //c style

cc.who_am_I();          //cc style

pc->who_am_I();        //c style

}

使用virtual关键字,编译器会在后台生成vtbl并且为每个对象设置vptr。

class Animal

{

       int name;

       bool isAnimal()

{

return true;

}

       virtual void sound()

{

       Cout<<”\n”;

}

       }

class Dog:public Animal

{

bool hasFur;

void sound()

{

 Cout<<”dog dog dog\n”;

}

virtual Void eat()

{

       Cout<<”eat meat and vegetable\n”;

}

}

class Wolf:public Dog

{

void sound()

{

 Cout<<”wolf wolf wolf\n”;

}

void eat()

{

       Cout<<”eat meat only\n”;

}

}

int main()

{

Animal animal,*pa;

Dog dog,*pdog;

Wolf wolf;

pa = &wolf;

pdog = &wolf;

pa->sound();        // wolf wolf wolf (触发多态机制)

pdog->eat();  // eat meat only (触发多态机制)

}

如图所示,每个对象的虚函数表各不相同。向上转型时编译器负责保证vptr指向正确的虚函数表。


building Android open source project

1.       编译环境的搭建。

首先是选择操作系统。强烈推荐64 Ubuntu(我在 10.04.4 LTS 上编译成功)。

理由如下,最开始尝试用32arch编译,过程苦不堪言,尝试了一天之后放弃了,需要改太多地方。然后图省事换成32位的ubuntu(刚好有块安装盘),按照官方的步骤apt了一个又一个的包。编译的途中开始抱怨x86_64-linux-gcc only run on 64-bit linux 这个错误是一切折腾的开始。动手改makefile,能编译发现不能链接,这个包能链接发现下个又不能链接。。。狠狠google了一番,发现google2.3开始就放弃了32位的开发环境。鼓励大家用64位的os编译android。刚开始还有不少人在hack代码,使之能在32位下使用。现在已经是4.1的版本了,还在32位下坚持的人应该不多了吧。

操作系统定下来之后,接下来就顺了。

根据官方的教程安装以下包

$ sudo apt-get install git-core gnupg flex bison gperf build-essential \
  zip curl zlib1g-dev libc6-dev lib32ncurses5-dev ia32-libs \
  x11proto-core-dev libx11-dev lib32readline5-dev lib32z-dev \
  libgl1-mesa-dev g++-multilib mingw32 tofrodos python-markdown \
  libxml2-utils xsltproc

sun-java6-jdk (官网上给的那个源已失效)

$ sudo add-apt-repository "deb http://us.archive.ubuntu.com/ubuntu/ hardy multiverse"
$ sudo apt-get update
$ sudo apt-get install sun-java6-jdk

至于ccache这种加速的东西,我推荐成功编译之后再使用,刚开始还是单纯的编译源码,省的配置错了导致莫名其妙的错误。

2.下载源码

下载repo工具

curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > repo

repo init -u https://android.googlesource.com/platform/manifest

(推荐第一次下载的时候加上mirror参数,好处多多,可以方便的共享给其他同学用,可以方便的供以后各种需求,而不用再次忍受从google服务器上下载的龟爬网速)

Repo sync 抓源码

(如果加了mirror参数,此时得到的是服务器上的完全镜像,不能直接使用。需要换个目录再次repo)

3. build and run

$ source build/envsetup.sh

$ lunch full-eng
$ make –j4 
#######some hours later#######
$emulator 

如果执行emulator提示找不到,需要手动设置一下环境变量,最简单的方法就是执行一遍 source build/envsetup.sh && lunch full-eng

 enjoy the open-source!

 

 

 

搭建java web开发环境(eclipse + WTP + Tomcat)

第一步是安装eclipse。

eclipse有诸多个版本,随便挑一个。http://www.eclipse.org/downloads/

基本的是sdk版。这个是最小插件版,只有一个java开发的插件。其他的集成版本有 jave ee、等等。我挑的基本sdk版。也就是classic

第二步是自己安装wtp(web tools platform)插件。

这里有个很坑爹的地方。去网上搜索eclipse安装wtp,都会告诉你在install new software里选某个某个,或者加某个某个网址,开始搜索。然后就各种错误。后来发现是因为eclipse sdk有多个版本,每个版本对应的更新地址是不一样的。版本的名字会在启动eclipse的时候显示,比如juno、indigo等等。各自对应不同的更新地址。只要看好自己的版本,用正确的地址进行搜索就没问题了。

第三步是安装Tomcat。

这里没太多问题,就是新版的wtp不支持太低版的tomcat,换个较新的下载就好了。

下载zip包解压缩出来就可以了。

末了补一句,以上需要jdk的支持。

之后新建 dynamic web project就可以了。