# asm-offset.h生成过程
Date: 2019-08-05


#### **asm-offset.h 文件**

  asm-offset.h 文件是在内核编译过程中生成的，生成过程  之前整理了一份笔记 [TASK\_threadsp的实现及asm-offsets.h，](https://garlicspace.com/2019/07/21/task-threadsp的实现及asm-offsets-h/)    其中涉及sed处理包含在内核代码 /scripts/Makefile.lib的sed-offsets中，工作是将生成的asm-offset.s转化为asm-offset.h。

```
/scripts/Makefile.lib
...
# ASM offsets
# ---------------------------------------------------------------------------

# Default sed regexp - multiline due to syntax constraints
#
# Use [:space:] because LLVM's integrated assembler inserts <tab> around
# the .ascii directive whereas GCC keeps the <space> as-is.
define sed-offsets
	's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:; \
	/^->/{s:->#\(.*\):/* \1 */:; \
	s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
	s:->::; p;}'
endef
```

<!--more-->

#### **sed用法整理：**

- ##### **命令行参数**
    
    - **\-n  ： 禁止自动打印**
        
    - **\-e  ：支持多行匹配输入**
        
- ##### **用s命令替换**
    

   s 命令（替代命令）是sed中最重要的一个命令，而且还支持很多不同选项，

- - **语法**：
        
        - _**s/regexp/replacement/flags**_
            

- - **匹配：**
        
        - 圆括号：replacement部分可以包含 \\ n（n是1到9的数字，包括1和9，\\1,\\2,...\\9），对应regexp中圆括号之间的匹配部分，pattern space 中第一个匹配的项标识为\\1 。
        - &符号：  replacement部分可以包含未转义的&字符。&表示正匹配的
    - **分隔符：**
        
        - s命令后的任意单个符号可以替换**分隔符 '/'**，做为新的分隔符 ， '/' 如果要出现在regexp或replacement时前面需要增加'\\'符号。
            

- - **flags：**
        - **g：**替换所有匹配条目
        - **p:** 打印 pattern space中的信息
        - w filename：将结果输出到文件

- ##### **正则表达式**
    

  [sed手册第5章](https://www.gnu.org/software/sed/manual/sed.html#BRE-syntax)对正则表达式有详细的描述，常见如下：    

| 特殊字符 | 描述 |
| --- | --- |
|  **^**      | 匹配输入字符串开始的位置 |
|  **$**    | 匹配输入字符串结尾的位置 |
|  **\*** | 匹配0个或多个在\*之前的表达式 |
| \\(regexp\\) | 1\. 后缀运算符符号， 如 \\(abcd\\)\* 将寻找0个或多个“abcd”整个字符序列   2\. 反向引用 |
| \[list\] | 匹配\[\]内的字符集合 |
| \[^list\] | 表示不匹配\[\]内的字符串 |
| .\* | 匹配所有字符串，包含空 |
| .\\+ | 匹配所有字符串，至少包含一个字符 |
| \\$ | 匹配单个$符号 |
| \[:alnum:\] | 字母数字 |
| \[:digit:\] | 数字 |
| \[:space:\] | 制表符(\\t) 换行符(\\n) 垂直制表符(\\v) 换页符(\\f) 回车符(\\r) 空格(\\s)。 |

  针对 Bracket Expressions ， 如\[:space:\] ， \[:alnum:\] 在使用时需要增加个\[\], 对sed新版本不增加的话会报错，对于老版本可以不加， 匹配的是\[\]中的集合， 非常幸运， 我的是老版本：），验证一下。 

```
## 1. 输入1， 不包含在“:digit:”中直接输出
[root@centosgpt generate]# echo 1|sed ' s/[:digit:]/X/'
1

## 2. 输入d， 包含在“:digit:”中替换为X
[root@centosgpt generate]# echo d|sed ' s/[:digit:]/X/'
X

##  3. 修改正则表达式数字1被替换为X
[root@centosgpt generate]# echo 1|sed ' s/[[:digit:]]/X/'
X
##  4. 改为输入d， 匹配失败，输出X
[root@centosgpt generate]# echo d |sed ' s/[[:digit:]]/X/'
d

## 下面是sed手册里关于新版本Bracket Expressions为增加[]的报错信息
# current GNU sed versions - incorrect usage rejected
$ echo 1 | sed 's/[:digit:]/X/'
sed: character class syntax is [[:space:]], not [:space:]
```

- ##### **用#命令注释**
    

#符号用来标识注释的开始

- ##### **用q命令退出**
    

退出不再处理其他指令

- ##### **用d命令删除**
    

删除模式空间，进入下一个周期

- ##### **用p命令打印**
    

打印模式空间一般与-n一起使用 -n将不再自动输出pattern space

- ##### **用n命令跳过单行**
    

跳过单行

- ##### **用{command}命令组合**
    

设置一组命令

```
## 1. 使用q命令退出
[root@centosgpt generate]# seq 3| sed 2q
1
2

## 2. 使用d命令删除
[root@centosgpt generate]# seq 3| sed 2d
1
3

## 3. 使用n命令跳过使用p打印
[root@centosgpt generate]# seq 6|sed -n  'n;n;p'
3
6
```

#### **sed-offsets分析：**

#####   **asm-offset.s片段：**

```
.ascii "->"
# 0 "" 2
# 16 "asm-offsets.c" 1

.ascii "->#TASK_threadsp1"
# 0 "" 2
# 17 "asm-offsets.c" 1

.ascii "->TASK_threadsp1 $0 offsetof(struct task_struct1, thread.sp)"
# 0 "" 2
# 18 "asm-offsets.c" 1

.ascii "->"
# 0 "" 2
#NO_APP
```

#####  **sed-offsets处理过程：**

- **sed脚本：**

```
's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:; \/^->/{s:->#\(.*\):/* \1 */:; \s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \s:->::; p;}'
```

- **第一条指令属于替换指令：**
    

```
 s:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;
```

**regexp部分分为6部分：**

1. - **^\[\[:space:\]\]\***   匹配0个或多个以空格，制表符，回车换行开头的行；
    -  **\\.ascii**      匹配 ".ascii" 字符串；
    -  **\[\[:space:\]\]\***  匹配0个或多个以空格，制表符，回车换行符；
    - **"\\(.\*\\)"**      上面匹配成功后，该行后续双引号""内部的字符串， 标记为引用 \\1
    - **.\***          匹配双引号后的任意字符串
    - **\\1**         引用regexp中原括号匹配的字符串

**处理结果：**

 **字符串“.ascii”对应的双引号内部的字符串替换本行如下**

```
## 1.command  (由于使用-n 所以增加了一个p， 用于显示每步的输出)

#cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;p'

## 2.output

->
# 0 "" 2
# 16 "asm-offsets.c" 1

->#TASK_threadsp1
# 0 "" 2
# 17 "asm-offsets.c" 1

->TASK_threadsp1 $0 offsetof(struct task_struct1, thread.sp)
# 0 "" 2
# 18 "asm-offsets.c" 1

->
# 0 "" 2
#NO_APP
```

- **第二条指令属于组合指令：**

```
/^->/{s:->#\(.*\):/* \1 */:; \s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \s:->::; p;}'
```

- **组合指令的第一条指令-替换指令：**

```
s:->#\(.*\):/* \1 */:;
```

设置"->#" 后的字符串设置为\\1 ，并将\\1内容放入注释段中

```
## 1.command  (由于使用-n 所以增加了一个p， 用于显示每步的输出)

# cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;/->/{s:->#\(.*\):/* \1 */:;p}'

## 2.output

->
/* TASK_threadsp1 */
->TASK_threadsp1 $0 offsetof(struct task_struct1, thread.sp)
->
```

- **组合指令的第二条指令-替换指令：**

```
s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;
```

**regexp部分分为四部分:**

- -  **->\\(\[^ \]\*\\)**    匹配将"->"开头的行，之后非空格的字符串做为第一个引用\\1 ；
    - \[\\$#\]\* **\\(\[^ \]\*\\)**   跳过一个空格后，过滤$ ,#字符后非空格的字符串做为第一个引用\\2；
    -    **\\(.\*\\)**     跳过一个空格，后续所有字符串设置为引用
    - **:#define \\1 \\2 /\* \\3 \*/:**; 组成C语言宏定义格式，并将\\3 做为注释

```
## 1.command  (由于使用-n 所以增加了一个p， 用于显示每步的输出)

# cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;/->/{s:->#\(.*\):/* \1 */:;s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;p}'

## 2.output

->
/* TASK_threadsp1 */
#define TASK_threadsp1 0 /* offsetof(struct task_struct1, thread.sp) */
->
```

- **组合指令的第三条指令-替换指令：**

```
s:->::
```

过滤满足存在"->"行

```
## 1.command  (由于使用-n 所以增加了一个p， 用于显示每步的输出)

#cat asm-offsets.s |sed -ne 's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:;/->/{s:->#\(.*\):/* \1 */:;s:^->\([^ ]*\) [\$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:;s:->::;p}'

## 2.output
/* TASK_threadsp1 */
#define TASK_threadsp1 0 /* offsetof(struct task_struct1, thread.sp) */

```

- **组合指令的第四条指令-打印：**

```
p
```

将处理结果输出

```
/* TASK_threadsp1 */#define TASK_threadsp1 0 /* offsetof(struct task_struct1, thread.sp) */
```

至此sed的任务顺利完成。

#### **参考：**

[sed, a stream editor](https://www.gnu.org/software/sed/manual/sed.html#sed-commands-list)

[SED 简明教程](https://coolshell.cn/articles/9104.html)

[Sed - An Introduction and Tutorial by Bruce Barnett](http://www.grymoire.com/Unix/Sed.html)

