之前安装过Bochs,但是本身也没有怎么用,看到网上说使用qemu效果好一些,也就简 简单单的安装下来。
$git clone
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
$cd linux
$ make menuconfig
#在kernel hacking中选中compile the kernel with debug info,
$ make bzImage
编译后,bzImage这个是被压缩了的,供qemu虚拟机使用,vmlinux里面带了某些信息, 没有压缩,供gdb使用。
git clone git://git.qemu-project.org/qemu.git
# Switch to the QEMU root directory.
cd qemu
# Prepare a native debug build.
mkdir -p bin/debug/native
cd bin/debug/native
# Configure QEMU and start the build.
../../../configure --enable-debug
make
# Return to the QEMU root directory.
cd ../../..
使用以下命令:
bin/debug/native/x86_64-softmmu/qemu-system-x86_64 -L pc-bios
同时应该将上面运行后的东西删除掉。
make clean
switch to the QEMU root directory
cd qemu
# Configure QEMU for x86_64 only - faster build
./configure --target-list=x86_64-softmmu --enable-debug
# Build in parallel - my system has 4 CPUs
make -j4
layout regs
显示的窗口如下:
┌──Register group: general─────────────────────────────────────────────────────────────────────────────────────┐
│zero 0x0 0 │
│ra 0x252ea 0x252ea <__real_libc_init(void*, void (*)(), int (*)(int, char**, char**), structors_a│
│sp 0x3ffffff640 0x3ffffff640 │
│gp 0xf4e28 0xf4e28 <je_extents_rtree+337096> │
│tp 0x1555568fc0 0x1555568fc0 │
│t0 0x253a8 152488 │
│t1 0x3c412 246802 │
│t2 0x8f2b8 586424 │
│fp 0x88028 0x88028 <__FINI_ARRAY__> │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
B+>│0x25078 <main+2> sd ra,8(sp) │
│0x2507a <main+4> auipc a0,0xfffec │
│0x2507e <main+8> addi a0,a0,1215 │
│0x25082 <main+12> auipc ra,0x22 │
│0x25086 <main+16> jalr -1426(ra) │
│0x2508a <main+20> li a0,0 │
│0x2508c <main+22> ld ra,8(sp) │
│0x2508e <main+24> addi sp,sp,16 │
│0x25090 <main+26> ret │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────┘
remote Thread 12044.12044 In: main L4 PC: 0x25078
(gdb)
如果想查看浮点寄存器,可以使用“tui reg float”命令
(gdb) b main
Breakpoint 1 at 0x5555555546e9: file my.c, line 21.
(gdb) c
Starting program: /home/yubo/test/my
Breakpoint 1, main () at my.c:21
21 n = 1;
(gdb) s
22 n++;
(gdb) p n
$1 = 1
在这段代码中,我设置了一个断点并且运行起来,注意,断点处,21,这个21的n=1还没有被执行,只是下一次将要执行的语句,例证就是22的代码,你看,在22处, 虽然打印了n++,但是你打印 p n
, 则还是n,说明n没有加1,所以这就是下一条语句要执行的过程。
#include<stdio.h>
int main()
{
int a[5] = { 1,2,3,4,5};
int *ptr1 = (int *)(&a + 1);
int *ptr2 = ((int *)(int)a + 1);
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}
显示源代码的num行的周围几行。
显示函数。
使用这个命令,还可以查看结构体的各元素值
p *pstDate
如果你想查看一个数组的各个元素,别像我似的傻乎乎的一个个 p a[i],节省生命的做 法应该是:
p ArrayVal@itemnum
什么意思?意思就是打印数组ArrayVal的前itemnum个元素。
p /x Val 十六进制
/c 字符?
/f 浮点型?
x
我也不知道这一个有什么作用现在为止
info local
使用show args查看设置的参数。
你可以在一个函数前面加上b,到时候就可以在这个函数设置断点了。
在源文件filename的linenum行设置断点
在源文件的filename的function设置断点
在程序运行的内存空间地址处停住.
没有参数时,表示在下一条指令处停住。
比如,设置
break if i = 10;
就是在循环体内当i等于10的时候停住。
方便一点的话,可以使用
filename:line_numbers if i == 5
比如, 我想在文件gdb.c的循环体for内当I等于5的停下来,则:
break gdb.c:6 if i == 5
在调用函数的那行,设置个断点,然后step就进入函数体内。
扩展的用法是next
运行程序直到当前函数完成返回,并打印函数的返回时的堆栈地址、返回值及参数值。
使用until退出循环体。这一条十分重要哈^_^.
stepi用于单步跟踪一条机器指令,另外使用
display/i $pc
在打印出程序代码的同时打印出机器指令。
watch
rwatch
awatch
info watchpoints: 列出当前所设置的所有观察点。
当我们使用 s 命令进入函数体内时,除了简单的 p 打印,还要更加丰富些,比如,使用backtrace(bt)
(gdb) bt
#0 add_range (low=1, high=10) at main.c:6
#1 0x080483c1 in main () at main.c:14
从这里可以看出add_range()函数是main函数调用的,main传进来的参数是low=1,high=10,main的帧为1,add_range()的帧为0,使用i(info)来看函数局部变量的值。
如果想查看main函数的当前局部变量也可以,先使用frame(f)命令,选择1号栈再查看局部变量的值:
(gdb) f 1
#1 0x080483c1 in main () at main.c:14
14 result[0] = add_range(1, 10);
(gdb) i locals
result = {0, 0, 0, 0, 0, 0, 134513196, 225011984, -1208685768, -1081160480,
...
-1208623680}
disassembles /rm
这个函数就会显示当前函数的汇编信息,其中,/r
是十六进制(raw instructions), /m
,如果你对intel 的汇编语法感兴趣,可以使用这个命令。
x/i $pc
这条命令是AT&T的语法格式的。
下面是一个简单的Hello.c的反汇编,
(gdb) disassemble /m main
Dump of assembler code for function main:
7 {
0x0000000000027936 <+0>: addi sp,sp,-16
8 int a=10;
9 int b=10;
10 printf("Hello world\n");
0x000000000002793a <+4>: auipc a0,0xfffea
0x000000000002793e <+8>: addi a0,a0,-1025 # 0x11539
0x0000000000027942 <+12>: auipc ra,0x22
0x0000000000027946 <+16>: jalr -1502(ra) # 0x49364 <puts(char const*)>
11 printf("int a+b = %d\n", sum(a, b));
0x000000000002794e <+24>: addi a0,a0,1573 # 0x13f6f
0x0000000000027952 <+28>: li a1,20
0x0000000000027954 <+30>: auipc ra,0x22
0x0000000000027958 <+34>: jalr -1664(ra) # 0x492d4 <printf(char const*, ...)>
12 return 0;
0x000000000002795c <+38>: li a0,0
0x000000000002795e <+40>: ld ra,8(sp)
0x0000000000027960 <+42>: addi sp,sp,16
0x0000000000027962 <+44>: ret
请仔细看这段代码,这是riscv64的反汇编:)
比如,我有一个结构体结构是这样的:
/*一个有权值的边集在图中*/
struct Edge{
int src, dest, weight;
};
/* V: 顶点数 E:边数,无向图*/
struct Graph{
int V,E;
struct Edge* edge;
};
先定义一个graph,在完成一次loop操作后,这时我想查看其中的各个域的值,可以一次一次的使用:
p graph@v
等进行查看,不过这样查看效率可是不高,所以在gdb中,需要使用脚本:
(gdb) set $i=5
(gdb) while ($i!=0)
>print graph->edge[$i]
>set $i = $i - 1
>end
$5 = {src = 2, dest = 4, weight = 7}
$6 = {src = 4, dest = 1, weight = 6}
$7 = {src = 0, dest = 4, weight = 5}
$8 = {src = 49, dest = 0, weight = 0}
$9 = {src = 3, dest = 2, weight = 4}
最近,在使用vps,经常需要经本地文件经scp
命令传到vps上去,
scp -P ssh-port filename user@ip:path
但是,用多了就觉得很笨.于是,经过简单的了解,解决了这个小问题。
首先,回顾一下bash-shell的几个参数:
$0 : ./test.sh 即命令本身,相当于c/c++的argc[0]
$1 : 第一个参数
$2 : 第二个参数
...
$# : 参数的个数,不包括命令本身
$@ : 参数本身的列表,也不包括命令本身
$* : 正常模式下同$@相同,但是加上引号,"$*" 将所有的参数解释成一个字符串,而
"$@"是一个参数数组
所以,我的上传文件的脚本scp.sh也就简简单单的出来了;
scp -P ssh-port $1 root@ip:/path-you-want-to-save/
然后,执行
sh scp.sh filename
然后,你就等待输入密码就可以了。
关于测试数据,本来不应该给予过多的关注,但是,很多时候我们在调试数据上面浪费 了很多的时间,下面,这篇文章我将持续的更新,介绍自己方法。
首先,写出一个头文件:
#include<stdlib.h>
/*
*
* 使用方法,只需在主函数中声明i,j;
* Right now, i dont have realize extern from "header.h",
* Only to use "cat macro.h >> filename" ;
* 2016-03-11
*
*/
#define print_maritx_int(n,a) \
for (i = 0; i < n ;i++) \
{ \
printf("\n"); \
for (j = 0; j < n; j++) \
printf("%d\t",a[i][j]); \
}
目前我还没有实现导入外部头文件的方法使用这个宏,我的本意是是在需要的源文件 插入:
extern print_maritx_int(n,a);
可惜,不给力,每次给我报错误,不知道是哪里的问题。
现在的解决方案很简单,就是这个头文件放在手头上,暂时将需要的宏用命令插进去。
* content
{:toc}
#!/bin/bash
#ss.sh
sslocal -s ip -p portnum -k password -t 600 -m aes-256-cfb
先说说有关的数据结构,内核中的这个数据结构映射的是物理地址,里面
有个count
的数据域,它标记着这个页的使用的量。返回值是-1,就说明
这个页没有被使用。然而,内核中经常使用一个page_conut()
的函数来
检查这个页的使用情况,通常情况是等于0(空闲),大于0(个数就是真实的
使用量)。
与page
有关的一个概念就是zone
了,可以说page
被分成了zone
,
zone
没有物理上的意义,只是为了方便追踪对page
的管理,页中的zone
有以下几个方面的应用:
ZONE_DMA
ZONE_DMA32
ZONE_NORMAL
ZONE_HIGHMEM
这些东西是定义在<include/linux/mmzone.h>
中的。
根据你使用的不同情况,合理的使用的标记。还有,zone
是不能垮边界的。
最常用的一个函数是
void *alloc_pages(gfp_t fp_mask, unsigned int order)
这样会分配一个2的order次方大小数量的页。gfp_t
是关于你分配页的使用的
标志,定义在<linux/gfp.h>里,它是__get_free_page()
的缩写。使用上面的函数,
我们得到的页是物理地址,
还不能被进程使用,我们必须转换成进程使用的虚拟地址,才能够真正应用这个
物理页。利用:
void *page_address(struct page *page)
就可以将分配到的物理页转化为虚拟地址,也就是进程可以使用的地址。
unsigned long get_zeroed_page(
unsigned int gfp_mask)
在某些情况下等于,返回一个全部充满0的页。
__get_free_page()
这个函数是分配以字节为单位的内核空间,与用户空间的malloc
函数特别相似。
函数定义在<linux/slab.h>,分配的也是2的方的数值。
void *kmalloc(size_t size, gfp_t flag)
分配的最大数值为128KB.
说说这个标志。它被定义在<linux/types.h>中,分为action modifiers、zone modifiers ,定义在<linux/gfp.h>中。
仅仅 ~alloc_page`可以分配高内存。
这个函数直接从虚拟地址分配地址,定义在<linux/vmalloc.h>文件中,且与 vfree()函数配套使用。
前面的分配页的内存管理方法可以满足绝大多数对内存的需要,可是有一点, 就是消耗的时间太多,因为经过了两级映射,有的甚至是三级映射,所以, 内核的开发者又想出了一个折中的方案,把页连成一串,形成页缓存。 在它下面使用
kmem_cache_create()
类似的函数,这里我们只需要简单的了解,更详细的我会补充上。
alloc_bootmem_..()
这是在启动的时候分配内存的代码,在初始化完成后这个代码就会被擦除。