linux4.15.y内核中的函数个数
依赖关系: vmlinux, vmlinux.bin, bzImage
cd /bal/linux-stable/
file vmlinux
#vmlinux: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]=b99bbd9dda1ec2751da246d4a7ae4e6fcf7d789b, not stripped
#文件大小 20MB, 19940148B
file arch/x86/boot/compressed/vmlinux.bin
#arch/x86/boot/compressed/vmlinux.bin: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, BuildID[sha1]=b99bbd9dda1ec2751da246d4a7ae4e6fcf7d789b, stripped
#文件大小 14MB, 13706624B
file arch/x86/boot/vmlinux.bin
#arch/x86/boot/vmlinux.bin: data
#文件大小 6.5MB, 6752568B
file arch/x86/boot/bzImage
#arch/x86/boot/bzImage: Linux kernel x86 boot executable bzImage, version 4.14.332+ (z@shinelon) #1 SMP Sat Jan 6 19:26:05 CST 2024, RO-rootFS, swap_dev 0X6, Normal VGA
#文件大小 6.5MB , 6767424B
编译产物ELF文件vmlinux 中的函数个数
cd /bal/linux-stable/
readelf -s vmlinux.o | grep FUNC | wc -l #39998
readelf -s vmlinux | grep FUNC | wc -l #39998
readelf -s vmlinux.o | grep OBJECT | wc -l #43012
readelf -s vmlinux | grep OBJECT | wc -l #42644
readelf -s vmlinux.o | wc -l #95312
readelf -s vmlinux | wc -l #85192
clang插件观测到的函数个数
sqlite3 -readonly ./SrcFileFuncIdGenService/fn.db "select count(*) from tbl_Func" #51284
vmlinux
grep "vmlinux" /bal/make.log
, 找到生成vmlinux的链接命令:
ld -m elf_i386 \
#指定目标架构为 32 位 x86 ELF 格式。
-z noexecstack \
#禁用栈的执行权限,提高安全性。
--emit-relocs \
#生成重定位信息。
--discard-none \
#不丢弃任何未使用的部分。
--build-id \
#为输出文件生成 Build ID。
-o vmlinux \
#指定输出文件名为 vmlinux。
-T ./arch/x86/kernel/vmlinux.lds \
#使用指定的链接脚本。
--whole-archive built-in.o --no-whole-archive \
#开始whole-archive ... 结束whole-archive, 即 强制包含 built-in.o 中的所有目标文件。
--start-group lib/lib.a arch/x86/lib/lib.a --end-group \
#将库文件 lib/lib.a 和 arch/x86/lib/lib.a 包围在一个组内,确保解析所有符号。
.tmp_kallsyms2.o # 输入的目标文件。
find /bal/linux-stable -path "*x86*Makefile" | xargs -I@ grep -Hn "vmlinux:" @
,找到有目标vmlinux的:
/bal/linux-stable/arch/x86/boot/Makefile:111:$(obj)/compressed/vmlinux: FORCE
/bal/linux-stable/arch/x86/boot/compressed/Makefile:118:$(obj)/vmlinux: $(vmlinux-objs-y) FORCE
System.map
grep "System.map" make.log
,主要有:/bin/bash ./scripts/mksysmap vmlinux System.map
cat /bal/linux-stable/scripts/mksysmap
, 此文件mksysmap只有一行:
$NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)\|\( \.L\)\|\( L0\)' > $2
,
带入参数后为:
$NM -n vmlinux | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)\|\( \.L\)\|\( L0\)' > System.map
chatgpt的解释开始
这个命令使用了
nm
命令来提取 Linux 内核 (vmlinux
) 的符号表信息,并通过grep
进行过滤,将结果保存到System.map
文件中。下面是各个过滤条件的含义:
-
[aNUw]
: 这是一个字符类,匹配符号的类型。在这里,它过滤掉了类型为小写 ‘a’、‘N’ 或 ‘w’ 的符号。这通常是与代码、未定义符号或弱符号相关的标志。 -
\(__crc_\)
: 匹配包含 ‘_crc’ 的符号。这样的符号通常是与内核中的 CRC (Cyclic Redundancy Check) 相关的。 -
\$[adt]
: 匹配以 ‘ a ′ 、 ′ a'、' a′、′d’ 或 ‘$t’ 开头的符号。通常这些符号表示代码、数据或文本段。 -
\.L
: 匹配以 ‘.L’ 开头的符号。这样的符号通常是由编译器生成的本地标签。 -
L0
: 匹配以 ‘L0’ 开头的符号。这也可能是编译器生成的本地标签。
通过这些过滤条件,命令排除了一些在内核符号表中不需要的信息,集中保留了主要的符号。这有助于提高 System.map 文件的可读性,使得在进行内核调试和分析时更容易找到感兴趣的符号。System.map 文件对于内核调试非常有用,因为它提供了符号名称和对应的内存地址,帮助开发人员理解和定位内核中的各种符号。
chatgpt的解释结束
makefile2graph 尝试可视化Makefile的依赖关系
sudo apt install makefile2graph
makefile2graph /bal/linux-stable/arch/x86/boot/compressed/Makefile > /bal/makefile2graph.txt
将文件makefile2graph.txt的内容粘到 https://dreampuf.github.io/GraphvizOnline,选osage、png:
得到一个14MB的图片,只能缩略传到这里:
, 实际图片
用makefile2graph直接可视化Makefile一定是不完整的,因为很显然:linux4是经过演化的,其编译过程的公共部分一定是抽象过的,而直观的依赖关系 可以体现在 编译过程中的(编译、链接)命令 序列。
类似于makefile2graph的工具有:
chatgpt回答开始
有几种工具可以用于将Makefile的依赖关系以图形化或其他方式展现出来。以下是一些常见的工具:
-
Graphviz:
- 描述: Graphviz 是一个开源的图形可视化工具集,可以用于绘制图形,包括依赖图。
- 使用方式: 你可以使用
dot
命令行工具来生成依赖图。例如:
其中,make -Bnd | make2graph | dot -Tpng -o makefile.png
make -Bnd
用于获取Makefile的依赖关系,make2graph
将其转换为Graphviz的输入格式,dot
用于生成图形。
-
Remake:
- 描述: Remake 是一个增强版的 Make 工具,支持交互式查看和修改Makefile的依赖关系。
- 使用方式: 通过运行
remake -x
进入交互模式,然后可以使用graph
命令生成依赖图。
-
CMake:
- 描述: CMake 是一个跨平台的构建系统,可以生成各种构建文件,包括依赖图。
- 使用方式: 通过在CMakeLists.txt文件中添加相关指令,然后运行CMake生成构建文件,并使用相应工具查看依赖图。
-
makefile2graph: (此即上面用的)
- 描述: makefile2graph 是一个专门为Makefile设计的工具,用于生成依赖图。
- 使用方式: 安装 makefile2graph 后,运行
makefile2graph yourMakefile | dot -Tpng -o makefile.png
来生成图形。
-
cmake-graph:
- 描述: cmake-graph 是一个用于可视化CMake项目的工具,也可以用于Makefile。
- 使用方式: 安装 cmake-graph 后,运行
cmake-graph yourMakefile | dot -Tpng -o makefile.png
来生成图形。
使用这些工具,你可以更好地理解和可视化Makefile中的依赖关系,以及构建过程中的各个步骤。选择适合你项目的工具,并根据需要进行配置。
chatgpt回答结束
结论
由此可见 , linux4.15.y内核编译过程中, clang插件观测到的函数个数(表tbl_Func中行数51284)比实际编译产物vmlinux中的函数个数(FUNC类型的符号数39998) 多了大约1万个函数
cmake 比较两次记录的所有变量差异
运行效果:
find_package(Clang REQUIRED CONFIG)
一共新增了约79个cmake变量,如下:
保存cmake当前变量们并与上次变量们比较,第【64】行,退出码:【0】, 标准输出: 【比较命令:‘diff /tmp/cmake_vars_1200 /tmp/cmake_vars_1201’ , 差异行数:79 /tmp/cmake_vars_diff_1200_1201】, 错误输出:【】
例子 文件 CMakeLists.txt
#文件名: CMakeLists.txt
#...
save_cmake_vars__cur_diff_prev(${CMAKE_CURRENT_LIST_LINE})
find_package(Clang REQUIRED CONFIG)
save_cmake_vars__cur_diff_prev(${CMAKE_CURRENT_LIST_LINE}) #此为第64行
#...
文件名: diff2f.sh
#文件名: diff2f.sh
#!/usr/bin/env bash
# me.sh ${_OutFPathPrefix} ${_prevVarsFIdx} ${gVarsFIdx}
#若参数少于3个,则退出(退出码为55)
[ $# -lt 3 ] && exit 55
_OutFPathPrefix=$1
_prevVarsFIdx=$2
gVarsFIdx=$3
_prevOutFPath="${_OutFPathPrefix}${_prevVarsFIdx}"
outFPath="${_OutFPathPrefix}${gVarsFIdx}"
resultF="${_OutFPathPrefix}diff_${_prevVarsFIdx}_${gVarsFIdx}"
echo -n "比较命令:‘diff ${_prevOutFPath} ${outFPath}’ , "
echo -n "差异行数:"
diff --side-by-side --suppress-common-lines ${_prevOutFPath} ${outFPath} > $resultF ;
wc -l $resultF | perl -pe 'chomp if eof'
#用命令 “ perl -pe 'chomp if eof' ” 删除wc输出的换行符号
文件名: save_vars__cur_diff_prev.cmake
#文件名: save_vars__cur_diff_prev.cmake
set(_Text_gVarsFIdxDesc "变量描述: 'cmake CACHE (INTERNAL)' 即 cmake全局(同级)变量")
set(_OutFPathPrefix "/tmp/cmake_vars_")
#如果从没有定义过全局变量gVarsFIdx 则定义该全局变量
if(NOT DEFINED gVarsFIdx)
set(gVarsFIdx 0 CACHE INTERNAL ${_Text_gVarsFIdxDesc})
# 'CACHE INTERNAL'变量gVarsFIdx 写入在文件 ./cmake-build-debug/CMakeCache.txt :
#gVarsFIdx:INTERNAL=数值
message(STATUS "初始化 gVarsFIdx=${gVarsFIdx}")
endif()
function(save_cmake_vars__cur_diff_prev _CMAKE_CURRENT_LIST_LINE)
# message(STATUS "函数进入 gVarsFIdx=${gVarsFIdx}")
MATH(EXPR _prevVarsFIdx "${gVarsFIdx} - 1")
# set(_prevOutFPath "${_OutFPathPrefix}${_prevVarsFIdx}")
set(_curVarsFIdx "${gVarsFIdx}")
# 获取 cmake变量们
get_cmake_property(_varNames VARIABLES)
# cmake变量们写入文件
set(_curOutFPath "${_OutFPathPrefix}${gVarsFIdx}")
file(WRITE ${_curOutFPath} "")
foreach(_varName ${_varNames})
if (_varName MATCHES "^_" #忽略_开头的变量名
OR _varName STREQUAL "gVarsFIdx") #忽略此脚本用到的变量名gVarsFIdx
# message(STATUS "忽略变量名【${_varName}】")
continue()
endif()
#向文件中写入一行
file(APPEND ${_curOutFPath} "${_varName}=${${_varName}}\n")
endforeach()
#文件名下标 自增1
MATH(EXPR _nextVarsFIdx "${gVarsFIdx} + 1")
set(gVarsFIdx ${_nextVarsFIdx} CACHE INTERNAL ${_Text_gVarsFIdxDesc})
#####
# 执行 diff 命令
execute_process(
# COMMAND bash -x ...
COMMAND bash /bal/clang-add-funcIdAsm/cmake_script/save_vars__cur_diff_prev/diff2f.sh ${_OutFPathPrefix} ${_prevVarsFIdx} ${_curVarsFIdx}
RESULT_VARIABLE shExitCode
OUTPUT_VARIABLE shStdOut
ERROR_VARIABLE shStdErr
)
message(STATUS "保存cmake当前变量们并与上次变量们比较,第【${_CMAKE_CURRENT_LIST_LINE}】行,退出码:【${shExitCode}】, 标准输出: 【${shStdOut}】, 错误输出:【${shStdErr}】")
#message(STATUS "函数出来 gVarsFIdx=${gVarsFIdx}")
endfunction()
# 调用函数保存所有变量
#save_cmake_vars__cur_diff_prev()
geogebra单位切向量(简单例子)
下载页面 —> GeoGebra5下载页面 --> GeoGebra5 Linux版本下载地址
或GeoGebra在线使用
启动
/app/GeoGebra-Linux-Portable-5-2-817-0/geogebra-portable
后 在 最底下"Input"输入栏 一条一条输入以下命令(不包括#开头的注释行):
cent=(0,0)
t=Slider(0,10)
r=Slider(1,5)
cir=Circle(Point(cent),r)
p_c=Point({r sin(2π t), r cos(2π t)})
#v_r=Vector(p_c)
#UnitPerpendicularVector(v_r) #这个向量起点不能指定,想要起点在p_c
v_{utp}=UnitVector(Tangent(p_c, cir))
再导出 gif动画
导出的gif动画如下