Skip to content

Commit

Permalink
新增笔记 “记一次 GP Debug 的心路历程”
Browse files Browse the repository at this point in the history
  • Loading branch information
Chiichen committed Oct 16, 2023
1 parent dd7664e commit c576bb4
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 11 deletions.
10 changes: 7 additions & 3 deletions src/.vuepress/navbar/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,15 @@ export const zhNavbar = navbar([
prefix: "杂谈/",
children: [
{
text: "震惊1=0!?",
text: "震惊,1=0",
icon: "page",
link: "编程杂谈/震惊,1=0!?.md",
link: "编程杂谈/震惊,1=0.md",
},
{
text: "记一次 GP Debug 的心路历程",
icon: "page",
link: "Debug杂谈/记一次 GP Debug 的心路历程.md",
},

],
},

Expand Down
2 changes: 1 addition & 1 deletion src/zh/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: 博客主页
heroImage: /logo.svg
heroText: Chi
heroFullScreen: true
tagline: 真正的大师永远都怀着一颗学徒的心
tagline: EveryBody dies but not everybody lives
projects:
- icon: project
name: DragonOS
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
title: 记一次 GP Debug 的心路历程
# cover: /assets/images/cover1.jpg

icon: page
order: 1
author: ChiChen
date: 2023-10-16
category:
- 杂谈
- Debug杂谈
tag:

- General Protection
- Debug
sticky: false
star: false
footer:
copyright: 转载请注明出处
---

## 背景

背景是在重构完 Signal 之后,一开始用我们自己的 libc 测试 signal 功能没有问题,但是在用 relibc 测试的时候出问题了,出错的原因就是一个 Userland 的 General Protection(GP),这个东西是一个硬件异常,通常是由于汇编指令出错或者内存访问出错导致,比如把一个未对齐的内存地址作为某个指令的操作数,而这个指令要求这个内存地址是 8/16 对齐的。

## 初步分析

在查看 GP 的 RIP ,反汇编了用户空间的测试程序之后,看到了对应的汇编代码是

```x86asm
movapd %XMM0 0xe0(rsp)
```

也就是说,这个地方是把寄存器的值写到栈上,查手册后得知, `movapd` 操作要求操作数是 `16 对齐`的,不对齐的话就会触发 GP ,现在问题就比较清晰了,大概就是 rsp 没有 16 对齐,接下来就是排查 rsp 的情况了。

>这里还发现了之前信号处理流程中没有正确保存浮点寄存器和清空浮点寄存器的bug,不过修复了这个bug之后还是没有解决 GP 的问题,所以这里先不提了。
## 进一步分析

在信号处理流程中,在设置用户栈帧的时候是需要手动修改传入的中断栈帧的一系列寄存器,包括 rip\rsp,并且保存这个上下文,然后在信号处理函数调用结束之后恢复的,所以这里很自然的就会开始排查,是不是 `setup_frame`,也就是设置栈帧的时候出了问题,比如没有 16对齐 rsp。但是经过一番排查之后,发现在 `setup_frame` 函数返回的时候,rsp 已经 16 对齐了,所以这里排除 `setup_frame` 函数的问题。然后就会想,是不是哪里写坏栈了,或者在 `sertup_frame` 函数返回的时候,用户空间函数执行之前,还有什么地方对 rsp 进行了修改。但是很快就排除了这个问题。

## 陷入僵局

到这就很奇怪了,返回的时候还是 16 对齐的,到了用户空间执行的时候就变成非16对齐的了。又想不到哪里会修改 rsp 的值,而且因为现在 DragonOS 还没有支持用户空间的 GDB 调试 (因为没有ptrace),所以我们就把这个用户程序 dump 出来,看里面的汇编对 rsp 是不是有操作的。

>这里我们又犯了一点小错误,就是在看汇编的时候,我们发现了在调用入口到报错位置有奇数个 push 指令,意思就是尽管传进来的是 16对齐但是到报错位置,rsp实际上就变成 8 对齐了,不过后来发现是在上一级调用的时候有个 callq,这个指令会压栈,所以实际上还是偶数个push指令,也就是与调用入口的对齐一致
然后发现也没什么异常,于是乎到这里就陷入僵局了。

## 跑着先

因为出错位置是在浮点周围,所以我们就选择在rust编译的时候开启软浮点,关闭硬件浮点支持,试试能不能跑起来。果不其然是能跑起来的,但是在 `sigreturn` 的时候报错了。于是这里又开始排查这个问题

## 好像

经过排查之后,发现是没有正确调用用户空间的 `sigreturn` 函数,也就没有调用内核的`sigreturn` 函数,然后打日志发现,用户空间传入内核的 `sig_restorer` (值恒为用户空间的 `sigreturn` 函数指针)有问题,但是在 relibc 中打日志发现是没问题的,到这一步就发现事情好像有点露出鸡脚了。

## 真相大白

然后 @longjin 提醒我检查内核的结构体是否与用户空间传入的结构体一致,我一看,真不一样,把这个一改,果然就跑起来了。开了硬件浮点之后之前的问题也不再出现了。但是为什么之前的 libc 能跑,这就是一个未解之谜了。经此一事,我们就要知道对于用户空间传入的数据要保持 100% 的敬畏之心,因为出现的错误很有可能千奇百怪。
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
name: 震惊1=0
name: 震惊,1=0
icon: edit
date: 2022-01-01
category:
Expand All @@ -9,7 +9,7 @@ tag:
- 杂谈/编程杂谈
---

# 震惊1=0!?
# 震惊,1=0

## 事情的起因

Expand All @@ -29,8 +29,8 @@ kdebug!("hander:{:?}, as u64:{:?}",frame.handler,frame.handler as u64)

我回来坐在凳子上,想着要不先测测别的能不能跑,也就是把原来错误的地址加上一个固定的值让它强行变到正确的地址上,再回头来解决这个问题,于是乎我就在测算这个值和正确的指针地址差多少,我在 `在线进制转换!` 中把输入的十进制数 `+1` ,没变!

![+1之前](./image/震惊1=0!?/image-1.png)
![+1之前](./image/震惊,1=0/image-1.png)

![+1之后](./image/震惊1=0!?/image-2.png)
![+1之后](./image/震惊,1=0/image-2.png)

看到这个结果的一瞬间我直接瘫坐在椅子上泣不成声。这是 google 中搜索 `进制转换` 排第一的网站,我测试了第一页全部网站,无一幸免。包括知名的 `菜鸟教程`。而我一旦搜索 `Dec converter` 随便两个网站都是正确的。我不做任何评价,但是这着实给我来了一点小小的中国震撼。
4 changes: 3 additions & 1 deletion src/zh/posts/编译原理/Chapter1 编译器组成.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ $$C(源代码)\rightarrow TAC(Three Address Code)中间(intermediate)码\rig

## 词法分析

- 本质上就是为了输出一个合法的token序列,每个token就是一个关键词或者一个关键词+词素(lexeme)的组合如$$\begin{array}{c}T\_While\quad 关键词\\ T\_Identifier \;\;x \quad 关键词+词素\end{array}$$
- 本质上就是为了输出一个合法的token序列,每个token就是一个关键词或者一个关键词+词素(lexeme)的组合如

$$\begin{array}{c}T\_While\quad 关键词\\ T\_Identifier \;\;x \quad 关键词+词素\end{array}$$

## 语法分析

Expand Down
7 changes: 5 additions & 2 deletions src/zh/posts/编译原理/Chapter2 词法分析.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ $\rightarrow T\_while \rightarrow( \rightarrow T\_IntConst+137\rightarrow<···

#### 字符表(alphabet)

- 是指一个由符号(字符、数字、特殊符号)组成的有限集,例如$$\sum=\{0,1\},A=\{a,b,c\}$$
- 是指一个由符号(字符、数字、特殊符号)组成的有限集,例如

$$\sum=\{0,1\},A=\{a,b,c\}$$

#### 字符串(string)

Expand Down Expand Up @@ -171,7 +173,8 @@ bool simulateDFA(string input){
#### 算法
- 将RE分为连续的子表达式(subexpression),每个括号内表示一个子表达式,可以用一个一般树来描述,越靠近根节点运算优先级越低$$ab|c^* \rightarrow ((ab)|(c^*))$$
- 将RE分为连续的子表达式(subexpression),每个括号内表示一个子表达式,可以用一个一般树来描述,越靠近根节点运算优先级越低
$$ab|c^* \rightarrow ((ab)|(c^*))$$
- 构建起基本子表达式的NFA
- 再将构建起来的NFA结合起来,形成最终的NFA
Expand Down

0 comments on commit c576bb4

Please sign in to comment.