kk Blog —— 通用基础


date [-d @int|str] [+%s|"+%F %T"]
netstat -ltunp
sar -n DEV 1

vmware 串口调试

在系统内核开发中,经常会用到串口调试,利用VMware的Virtual Machine更是为调试系统内核如虎添翼。那么怎么搭建串口调试环境呢?

Virtual Machine 主要有三种串口调试技术,可以在serial port的配置界面找到:

  1. Use physical serial port 即使用物理机串口,当用串口线盒另一台电脑连接时,就用这种方式
  2. Use output file 即把串口数据输出到宿主机某文件中,当只需要看输出结果的时候可以用这种方式,简单方便
  3. Use named pipe 把串口输出到命名管道,命名管道可读可写,也就可以交互,进行一些debug工作,而不是简单的看结果

因为前两种相对简单易用就不具体介绍了,这里主要说第三种用命名管道调试方法。命名管道,在Linux中是进程间通信(IPC)的一种方式,两个进 程可以通过读写管道来交换数据。这里就是运用了这种技术,通过把串口绑定一个有名管道,对有名管道的读写交换串口数据。也有两种方式:1. 宿主机与虚拟机之间, 2. 在同一宿主机上,两虚拟机间通过绑定同一个宿主机上的有名管道。问题的关键在于如何把虚拟机串口绑定到宿主机的某一有名管道,而第一种方式则需要找到一种 方式使得主机如何读写有名管道来交互,经过一阵Google终于找到分别在Linux和Windows分别试验成功的工具。

在Windows中有名管道式通过路径//./pipe/namedpipe来创建的,当然你可以指定到其他机子如//192.168.1.10 /pipe/namedpipe,而在Linux中,/tmp/mypipe就可以了。创建好有名管道后,就是如何和管道交互了。目前,无论是 Windows还是Linux,似乎都没有一款工具可以直接读写有名管道的,而我找到的两个工具都是通过把有名管道和Socket绑定,通过读写 Socket来间接读写管道。

下面我就简要介绍一下在Windows和Linux下如何配置:

Linux Host:
Host ~ Virtual Machine
1
2
3
4
5
6
7
8
9
10
11
1. configure VM
a. add hardware -> Serial port
b. using named pipe
c. /tmp/isocket
d. this end is server & far end is application
e. check Yield CPU on poll
f. start Virtual Machine

2. socat /tmp/isocket tcp4-listen:9001 &
/tmp/socket: VMware call it 'named piped', actually it is Unix Daemon Socket, so you shouldn't use pipe:/tmp/socket
3. telnet 127.0.0.1 9001

Trouble Shoot: 有时候会遇到错误Connection closed by foreign host,或者telnet一开,socat就能退出,很可能是你没power on虚拟机,有名管道还没创建,你就socat,这样也会创建一个名为isocket的文件但只是普通文件。具体的细节请看socat help
start Virtual Machine first, than run the socat, and telnet
(Note you must have permission to all resource, /tmp/socket, VM and so on)

Vritual Machine ~ Virtual Machine
1
2
3
4
5
6
7
8
9
10
11
12
13
1. configure VM
a. add hardware --> serial port
b. Using named pipe, configure /tmp/isocket 
c.  this end is server & far end is Virtual Machine
d. check Yield CPU on poll
e. start VM

2. Another VM
a. add hardware  -->  Serial Port
b. Using named pipe, configure /tmp/isocket 
c.  this end is client & far end is Virtual Machine 
d. check Yield CPU on poll
e. start VMs
Windows Host:
Host ~ Virtual Machine
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. configure VM
a. add hardware --> serial port
b. using named pipe
c. //./pipe/vmwaredebug
d. this end is client & far end is application
e. check Yield CPU on poll

2. using 3rd-party tool to communicate with named pipe
a. down the tool 
b. install service
cmd>vmwaregateway.exe /r
c. start service
c:/> net start vmwaregateway
d. telnet 127.0.0.1 567
3. start Virtual Machine

如果你使用的是vmwaregateway.exe这个小工具,这里的管道名就必须是vmwaredebug,除非你把它的源代码download下来自己改改。

Vritual Machine ~ Virtual Machine
1
2
3
4
5
6
7
8
9
10
11
12
13
1. configure VM
a. add hardware --> serial port
b. Using named pipe, //./pipe/vmwaredebug
c.  this end is server & far end is Virtual Machine
d. check Yield CPU on poll
e. start VM

2. Another VM
a. add hardware  -->  Serial Port
b. Using named pipe, //./pipe/vmwaredebug
c.  this end is client & far end is Virtual Machine 
d. check Yield CPU on poll
e. start VMs

独立的调试符号文件

这种将可执行程序与调试符号分离的方案好处多多。一方面,缩减了可执行程序的文件大小,在一定程度上提高了程序的执行性能,另一方面,对应的调试符号文件也方便了一些不时之需。本文就来看一下与此相关的两个问题。

一 如何给应用程序创建对应的调试符号文件?

这很简单,看个演示实例。有代码如下:

1
2
3
4
5
6
7
[root@lenky gdb]# cat t.c
#include <stdio.h> 
int main(int argc, char *argv[])
{
  printf("Hello world!\n");
  return 0;
}

依次执行命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@lenky gdb]# ls -l
total 4
-rw-r--r--. 1 root root 103 Mar 20 07:52 t.c
[root@lenky gdb]# gcc -g t.c -o t
[root@lenky gdb]# ls -l
total 12
-rwxr-xr-x. 1 root root 7717 Mar 20 07:58 t
-rw-r--r--. 1 root root  103 Mar 20 07:52 t.c
[root@lenky gdb]# objcopy --only-keep-debug t t.debuginfo
[root@lenky gdb]# objcopy --strip-debug t
[root@lenky gdb]# objcopy --add-gnu-debuglink=t.debuginfo t
[root@lenky gdb]# ls -l
total 20
-rwxr-xr-x. 1 root root 6470 Mar 20 07:58 t
-rw-r--r--. 1 root root  103 Mar 20 07:52 t.c
-rwxr-xr-x. 1 root root 6109 Mar 20 07:58 t.debuginfo

OK,可执行程序t和对应的调试符号文件t.debuginfo就生成了。

几条命令,给以分别解释一下:
1. gcc -g t.c -o t
这个无需多说,值得注意的是,-g和-O2可以同时使用。
2. objcopy –only-keep-debug t t.debuginfo
将可执行程序文件t内的与调试相关的信息拷贝到文件t.debuginfo内。也可以这样:
cp t t.debuginfo
strip –only-keep-debug t.debuginfo
3. objcopy –strip-debug t
删除可执行程序文件t内的调试相关信息。也可以直接使用strip命令,不过strip命令会把symtab也删除掉,导致在没有debuginfo文件的情况下,打印堆栈信息会受到影响,比如变得不那么清晰。
4. objcopy –add-gnu-debuglink=t.debuginfo t
在可执行程序文件t内添加一个名为.gnu_debuglink的section段,该段内包含有debuginfo文件的name名称和checksum校验和,以确保后续在进行实际调试时,可执行程序和对应的调试符号文件是一致的。

二 如何使用gdb调试带有调试符号文件的应用程序?

其实想想也知道,这只需解决一个问题,即如何把应用程序与调试符号文件关联起来。
gdb会按照一定的规则去搜索对应路径,找寻应用程序的调试符号文件,比如gdb会自动查找可执行程序所在目录下的.debug文件夹:

1
2
3
4
5
6
[root@lenky ~]# pwd
/root
[root@lenky ~]# gdb /home/work/gdb/t -q
Reading symbols from /home/work/gdb/t...Reading symbols from /home/work/gdb/.debug/t.debug...done.
done.
(gdb)

把调试符号文件放到同一个目录也可以:

1
2
3
4
5
6
[root@lenky ~]# rm -fr /tmp/.debug/
[root@lenky ~]# cp /home/work/gdb/.debug/t.debug /tmp/
[root@lenky ~]# gdb /tmp/t -q
Reading symbols from /tmp/t...Reading symbols from /tmp/t.debug...done.
done.
(gdb)

下面再介绍另外几种主动设置方法:

1,通过gdb启动参数-s指定:
1
2
3
[root@lenky ~]# gdb -s /home/work/gdb/.debug/t.debug -e /tmp/t -q
Reading symbols from /home/work/gdb/.debug/t.debug...done.
(gdb)

注意:可执行程序必须通过-e指定,否则貌似gdb会拿它覆盖-s参数,比如如下:

1
2
3
4
5
[root@lenky ~]# gdb -s /home/work/gdb/.debug/t.debug /tmp/t -q
Reading symbols from /tmp/t...Missing separate debuginfo for /tmp/t
Try: yum --disablerepo='*' --enablerepo='*-debuginfo' install /usr/lib/debug/.build-id/01/f1df7f4971caacd934aca9523c4e4b5ae95332.debug
(no debugging symbols found)...done.
(gdb)

可以看到,gdb直接尝试从文件/tmp/t内读取符号了,而不是文件t.debug。

2,利用gdb的命令设置搜索路径:set debug-file-directory directories

这是gdb官方文档提到的,可以设置搜索路径的命令,但是貌似并没有起作用,或者是我漏掉了什么。具体不说了,请看参考5。

参考:

  1. How to generate gcc debug symbol outside the build target?
    http://stackoverflow.com/questions/866721/how-to-generate-gcc-debug-symbol-outside-the-build-target
  2. Creating separate debug info
    https://blogs.oracle.com/dbx/entry/creating_separate_debug_info
  3. man objcopy
  4. .gnu_debuglink or Debugging system libraries with source code
    https://blogs.oracle.com/dbx/entry/gnu_debuglink_or_debugging_system
  5. 18.2 Debugging Information in Separate Files
    http://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
  6. http://www.technovelty.org/code/separate-debug-info.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#!/bin/sh
# 可以改成:将要提取的文件加到参数中

#set -x

objcopyout()
{
#       if [ -f `file -N $1 | sed -n -e 's/^\(.*\):[   ]*.*ELF.*, not stripped/\1/p'` ]; then
	    val=`objdump -s -j .gnu_debuglink "$1" | grep 'Contents of section .gnu_debuglink:'`
	    if [ -z "$val" ]; then
	            #debug_out=".debug/$1.debug".`date +%s`
	            tmp_name=`echo "$1" | awk -F/ '{ print $NF}'`
	            debug_out=".debug/$tmp_name.debug".`date +%s`
	            objcopy --only-keep-debug "$1" "$debug_out"
	            objcopy --strip-debug "$1"
	            objcopy --add-gnu-debuglink="$debug_out" "$1"
	    fi
#       objdump -s -j .gnu_debuglink "$1"
}

mkdir -p .debug/

while [ $# -gt 0 ]; do
	    if [ -f "$1" ]; then
	            objcopyout "$1"
	    fi
	    shift
done

#find . -name *.ko -print |
#while read f
#do
#       objcopyout "$f"
#done

objcopy命令介绍

objcopy把一种目标文件中的内容复制到另一种类型的目标文件中.

(1)将图像编译到可执行文件内

Q: 如何将一个二进制文件,比如图片,词典一类的东西做为.o文件,直接链接到可执行文件内部呢?
A:

1
2
3
4
5
6
$ objcopy -I binary -O elf32-i386 -B i386 14_95_13.jpg image.o
$ gcc image.o tt.o -o tt
$ nm tt | grep 14_95
0805d6c7 D _binary_14_95_13_jpg_end
00014213 A _binary_14_95_13_jpg_size
080494b4 D _binary_14_95_13_jpg_start

(2)使用objcopy把不用的信息去掉:

1
$ objcopy -R .comment -R .note halo halo.min

(3)

1
2
3
4
$ objcopy -R .note -R .comment -S -O binary xyb xyb.bin
-R .note -R .comment 表示移掉 .note 与 .comment 段
-S 表示移出所有的标志及重定位信息
-O binary xyb xyb.bin 表示由xyb生成二进制文件xyb.bin

objcopy工具使用指南

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
objcopy Utility
objcopy [ -F bfdname | --target=bfdname ]
[ -I bfdname | --input-target=bfdname ]
[ -O bfdname | --output-target= bfdname ]
[ -S | --strip-all ] [ -g | --strip-debug ]
[ -K symbolname | --keep-symbol= symbolname ]
[ -N symbolname | --strip-symbol= symbolname ]
[ -L symbolname | --localize-symbol= symbolname ]
[ -W symbolname | --weaken-symbol= symbolname ]
[ -x | --discard-all ] [ -X | --discard-locals ]
[ -b byte | --byte= byte ]
[ -i interleave | --interleave= interleave ]
[ -R sectionname | --remove-section= sectionname ]
[ -p | --preserve-dates ] [ --debugging ]
[ --gap-fill= val ] [ --pad-to= address ]
[ --set-start= val ] [ --adjust-start= incr ]
[ --change-address= incr ]
[ --change-section-address= section{=,+,-} val ]
[ --change-warnings ] [ --no-change-warnings ]
[ --set-section-flags= section= flags ]
[ --add-section= sectionname= filename ]
[ --change-leading char ] [--remove-leading-char ]
[ --weaken ]
[ -v | --verbose ] [ -V | --version ] [ --help ]
input-file [ outfile ]  

GNU实用工具程序objcopy的作用是拷贝一个目标文件的内容到另一个目标文件中。Objcopy使用GNU BFD库去读或写目标文件。Objcopy可以使用不同于源目标文件的格式来写目的目标文件(也即是说可以将一种格式的目标文件转换成另一种格式的目标文 件)。通过以上命令行选项可以控制Objcopy的具体操作。

Objcopy在进行目标文件的转换时,将生成一个临时文件,转换完成后就将这个临 时文件删掉。Objcopy使用BFD做转换工作。如果没有明确地格式要求,则Objcopy将访问所有在BFD库中已经描述了的并且它可以识别的格式, 请参见《GNUpro Decelopment Tools》中“using ld”一章中“BFD库”部分和“BFD库中规范的目标文件格式”部分。

通过使用srec作为输出目标(使用命令行选项-o srec),Objcopy可以产生S记录格式文件。

通过使用binary作为输出目标(使用命令行选项-o binary),Objcopy可以产生原始的二进制文件。使用Objcopy产生一个原始的二进制文件,实质上是进行了一回输入目标文件内容的内存转 储。所有的符号和重定位信息都将被丢弃。内存转储起始于输入目标文件中那些将要拷贝到输出目标文件去的部分的最小虚地址处。

使用Objcopy生成S记录格式文件或者原始的二进制文件的过程中,-S选项和-R选项可能会比较有用。-S选项是用来删掉包含调试信息的部分,-R选项是用来删掉包含了二进制文件不需要的内容的那些部分。

input-file
outfile
参数input-file和outfile分别表示输入目标文件(源目标文件)和输出目标 文件(目的目标文件)。如果在命令行中没有明确地指定outfile,那么Objcopy将创建一个临时文件来存放目标结果,然后使用input- file的名字来重命名这个临时文件(这时候,原来的input-file将被覆盖)。

-I bfdname
–input-target=bfdname
明确告诉Objcopy,源文件的格式是什么,bfdname是BFD库中描述的标准格式名。这样做要比“让Objcopy自己去分析源文件的格式,然后去和BFD中描述的各种格式比较,通过而得知源文件的目标格式名”的方法要高效得多。

-O bfdname
–output-target= bfdname
使用指定的格式来写输出文件(即目标文件),bfdname是BFD库中描述的标准格式名。

-F bfdname
–target= bfdname
明确告诉Objcopy,源文件的格式是什么,同时也使用这个格式来写输出文件(即目标文件),也就是说将源目标文件中的内容拷贝到目的目标文件的过程中,只进行拷贝不做格式转换,源目标文件是什么格式,目的目标文件就是什么格式。

-R sectionname
–remove-section= sectionname
从输出文件中删掉所有名为sectionname的段。这个选项可以多次使用。
注意:不恰当地使用这个选项可能会导致输出文件不可用。

-S
–strip-all (strip 剥去、剥)
不从源文件中拷贝重定位信息和符号信息到输出文件(目的文件)中去。

-g
–strip-debug
不从源文件中拷贝调试符号到输出文件(目的文件)中去。

–strip-undeeded
剥去所有在重定位处理时所不需要的符号。

-K symbolname
–keep-symbol= symbolname
仅从源文件中拷贝名为symbolname的符号。这个选项可以多次使用。

-N symbolname
–strip-symbol= symbolname
不从源文件中拷贝名为symbolname的符号。这个选项可以多次使用。它可以和其他的strip选项联合起来使用(除了-K symbolname | –keep-symbol= symbolname外)。

-L symbolname
–localize-symbol= symbolname
使名为symbolname的符号在文件内局部化,以便该符号在该文件外部是不可见的。这个选项可以多次使用。

-W symbolname
-weaken-symbol= symbolname
弱化名为symbolname的符号。这个选项可以多次使用。

-x
–discard-all (discard 丢弃、抛弃)
不从源文件中拷贝非全局符号。

-X
–discard-locals
不从源文件中拷贝又编译器生成的局部符号(这些符号通常是L或 . 开头的)。

-b byte
–byte= byte
Keep only every byte of the input file (header data is not affected). byte can be
in the range from 0 to interleave-1, where interleave is given by the -i or
–interleave option, or the default of 4. This option is useful for creating files to
program ROM . It is typically used with an srec output target.

-i interleave
–interleave= interleave (interleave 隔行、交叉)
Only copy one out of every interleave bytes. Select which byte to copy with the
-b or –byte option. The default is 4. objcopy ignores this option if you do not
specify either -b or –byte.

-p
–preserve-dates (preserve 保存、保持)
设置输出文件的访问和修改日期和输入文件相同。

[ –debugging ]
如果可能的话,转换调试信息。因为只有特定的调试格式被支持,以及这个转换过程要耗费一定的时间,所以这个选项不是默认的。

–gap-fill= val
使用内容val填充段与段之间的空隙。通过增加段的大小,在地址较低的一段附加空间中填充内容val来完成这一选项的功能。

–pad-to= address
填充输出文件到虚拟地址address。通过增加输出文件中最后一个段的大小,在输出文件中最后一 段的末尾和address之间的这段附加空间中,用–gap-fill= val选项中指定的内容val来填充(默认内容是0,即没有使用–gap-fill= val选项的情况下)。

–set-start= val
设置新文件(应该是输出文件吧?)的起始地址为val。不是所有的目标文件格式都支持设置起始地址。

–change-start = incr
–adjust-start= incr
通过增加值incr来改变起始地址。不是所有的目标文件格式都支持设置起始地址。

–change-addresses incr
–adjust-vma incr
Change the VMA and LMA addresses of all sections, section., as well as the
start address, by adding incr. Some object file formats do not permit section
addresses to be changed arbitrarily.

通过加上一个值incr,改变所有段的VMA(Virtual Memory Address运行时地址)和LMA(Load Memory Address装载地址),以及起始地址。某些目标文件格式不允许随便更改段的地址。

–change-section-address section{=,+,-} val
–adjust-section-vma section{=,+,-} val
设置或者改变名为section的段的VMA(Virtual Memory Address运行时地址)和LMA(Load Memory Address装载地址)。如果这个选项中使用的是“=”,那么名为section的段的VMA(Virtual Memory Address运行时地址)和LMA(Load Memory Address装载地址)将被设置成val;如果这个选项中使用的是“-”或者“+”,那么上述两个地址将被设置或者改变成这两个地址的当前值减去或加上 val后的值。如果在输入文件中名为section的段不存在,那么Objcopy将发出一个警告,除非–no-change-warnings选项被 使用。

这里的段地址设置和改变都是输出文件中的段相对于输入文件中的段而言的。例如:
(1)–change-section-address .text = 10000
这里是指将输入文件(即源文件)中名为.text的段拷贝到输出文件中后,输出文件中的.text段的VMA(Virtual Memory Address运行时地址)和LMA(Load Memory Address装载地址)将都被设置成10000。
(2)–change-section-address .text + 100
这 里是指将输入文件(即源文件)中名为.text的段拷贝到输出文件中后,输出文件中的.text段的VMA(Virtual Memory Address运行时地址)和LMA(Load Memory Address装载地址)将都被设置成以前输入文件中.text段的地址(当前地址)加上100后的值。

–change-section-lma section{=,+,-} val
仅设置或者改变名为section的段的 LMA(Load Memory Address装载地址)。一个段的LMA是程序被加载时,该段将被加载到的一段内存空间的首地址。通常LMA和VMA(Virtual Memory Address运行时地址)是相同的,但是在某些系统中,特别是在那些程序放在ROM的系统中,LMA和VMA是不相同的。如果这个选项中使用的是 “=”,那么名为section的段的LMA(Load Memory Address装载地址)将被设置成val;如果这个选项中使用的是“-”或者“+”,那么LMA将被设置或者改变成这两个地址的当前值减去或加上val 后的值。如果在输入文件中名为section的段不存在,那么Objcopy将发出一个警告,除非–no-change-warnings选项被使用。

–change-section-vma section{=,+,-} val
仅设置或者改变名为section的段的 VMA(Load Memory Address装载地址)。一个段的VMA是程序运行时,该段的定位地址。通常VMA和LMA(Virtual Memory Address运行时地址)是相同的,但是在某些系统中,特别是在那些程序放在ROM的系统中,LMA和VMA是不相同的。如果这个选项中使用的是 “=”,那么名为section的段的LMA(Load Memory Address装载地址)将被设置成val;如果这个选项中使用的是“-”或者“+”,那么LMA将被设置或者改变成这两个地址的当前值减去或加上val 后的值。如果在输入文件中名为section的段不存在,那么Objcopy将发出一个警告,除非–no-change-warnings选项被使用。

–change-warnings
–adjust-warnings
如果命令行中使用了–change- section-address section{=,+,-} val或者–adjust-section-vma section{=,+,-} val,又或者–change-section-lma section{=,+,-} val,又或者–change-section-vma section{=,+,-} val,并且输入文件中名为section的段不存在,则Objcopy发出警告。这是默认的选项。

–no-chagne-warnings
–no-adjust-warnings
如果命令行中使用了–change- section-address section{=,+,-} val或者–adjust-section-vma section{=,+,-} val,又或者–change-section-lma section{=,+,-} val,又或者–change-section-vma section{=,+,-} val,即使输入文件中名为section的段不存在, Objcopy也不会发出警告。

–set-section-flags section=flags
为section的段设置一个标识。这个flags变量的可以取逗号分隔的多个标识名字符串(这些标识名字符串是能够被Objcopy程序所识别的),合法的标识名有alloc,load,readonly,code,data和rom。 You can set the contents flag for a section which does not havecontents, but it is not meaningful to clear the contents flag of a section which does have contents; just remove the section instead. Not all flags are meaningful for all object file formats.

–add-section sectionname=filename
进行目标文件拷贝的过程中,在输出文件中增加一个名为 sectionname的新段。这个新增加的段的内容从文件filename得到。这个新增加的段的大小就是这个文件filename的大小。只要输出文 件的格式允许该文件的段可以有任意的段名(段名不是标准的,固定的),这个选项才能使用。

–change-leading-char
Some object file formats use special characters at the start of symbols. The most
common such character is underscore, which compilers often add before every
symbol. This option tells objcopy to change the leading character of every
symbol when it converts between object file formats. If the object file formats use
the same leading character, this option has no effect. Otherwise, it will add a
character, or remove a character, or change a character, as appropriate.

–remove-leading-char
If the first character of a global symbol is a special symbol leading character used
by the object file format, remove the character. The most common symbol leading
character is underscore. This option will remove a leading underscore from all
global symbols. This can be useful if you want to link together objects of different
file formats with different conventions for symbol names.

–weaken
Change all global symbols in the file to be weak. This can be useful when building
an object that will be linked against other objects using the -R option to the linker.
This option is only effective when using an object file format that supports weak
symbols.

-V
–version
Show the version number of objcopy.

-v
–verbose
Verbose output: list all object files modified. In the case of archives, objcopy -V
lists all members of the archive.

–help
Show a summary of the options to objcopy.