惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
The Exploit Database - CXSecurity.com
F
Fortinet All Blogs
U
Unit 42
F
Full Disclosure
雷峰网
雷峰网
博客园 - 司徒正美
云风的 BLOG
云风的 BLOG
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Tailwind CSS Blog
The Cloudflare Blog
Last Week in AI
Last Week in AI
罗磊的独立博客
D
DataBreaches.Net
C
Check Point Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
O
OpenAI News
C
CXSECURITY Database RSS Feed - CXSecurity.com
aimingoo的专栏
aimingoo的专栏
S
Security @ Cisco Blogs
大猫的无限游戏
大猫的无限游戏
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
The Hacker News
The Hacker News
Webroot Blog
Webroot Blog
Security Latest
Security Latest
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google DeepMind News
Google DeepMind News
酷 壳 – CoolShell
酷 壳 – CoolShell
N
News | PayPal Newsroom
P
Proofpoint News Feed
B
Blog RSS Feed
MongoDB | Blog
MongoDB | Blog
C
Cybersecurity and Infrastructure Security Agency CISA
N
News and Events Feed by Topic
Google Online Security Blog
Google Online Security Blog
H
Help Net Security
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
GbyAI
GbyAI
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
M
MIT News - Artificial intelligence
Vercel News
Vercel News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
MyScale Blog
MyScale Blog
腾讯CDC

博客园 - 蓝天上的云℡

事件编程之eventfd 事件驱动编程模式之EventLoop syslog日志查看器GUI图形界面,支持Windows/Linux/Macos开箱即用 ERRNO链条分析 ARM AMBA总线之AXI系 Linux Kernel Init Bootstrap 内核初始化启动 RK芯片平台GPIO控制 Linux USB子系统之Gadget设备端驱动 远程调试桥接-网络-ADB-GDB 斐讯N1盒子安装飞牛FNOS NAS 安卓以太网链路检测 安卓原生开发-Recovery使用minui绘制图形 蓝牙音频协议——安卓开发 RKxx系列的RK628摄像头配置影响HDMI出图 usb储存之BOT/UAS内核驱动 现代操作系统-音频处理技术1 Linux驱动底层 Linux驱动适配I2C/SPI例子 模块编译的pr_debug Linux内核/GKI内核主线更新-usb网卡名称变更引出的BUG 为Kernel代码生成VSCODE索引,快速跳转 安卓开机时间/性能优化 SGDMA与普通DMA Linux内核源码-存储驱动之 QSPI Flash golang数据转换技巧
Linux/Golang/glibC系统调用
蓝天上的云℡ · 2024-05-18 · via 博客园 - 蓝天上的云℡

Linux/Golang/glibC系统调用

本文主要通过分析Linux环境下Golang的系统调用,以此阐明整个流程

有时候涉略过多,反而遭到质疑~,写点文章证明自己实力也好

Golang系统调用

找个函数来分析
https://pkg.go.dev/os/exec#Cmd.Wait

源码文件在src/os目录下的: exec.go -> exec_unix.go -> pidfd_linux.go
https://github.com/golang/go/blob/2f6426834c150c37cdb1330b48e9903963d4329c/src/os/exec.go#L134
go/src/os
/exec.go

go/src/os
/exec_unix.go

go/src/os
/pidfd_linux.go

往下是系统调用: src/syscall目录的 syscall_linux.go -> ``
go/src/syscall
/syscall_linux.go

image

runtime层的:src/internal/runtime/syscall/syscall_linux.go,如下图,可以看见Sysacll6只有声明没有函数体,是个外部声明。
src/internal/runtime/syscall/syscall_linux.go

其函数体内容实际上位于同目录下的 .s 汇编文件,与编译时采用的架构工具链相关。

by the way: 这里的语法是Golang汇编,属于Plan9分支。
golang汇编参考资料:

  1. 官网资料 https://go.dev/doc/asm
  2. 简洁概述 https://hopehook.com/post/golang_assembly/

总结:Golang直接了当地使用汇编实现了系统调用(软中断号),而不需要再通过 libc 去调用系统调用库。这样的好处是不需要考虑 glibc 繁杂沉重的兼容性方案。

Linux 定义的系统调用表

本地审计工具:ausyscall --dump

Linux系统调用

内核实现

通过软中断陷入内核态/特权模式
和STM32 ARM核心一样,都是由一个异常向量表描述中断对应的Handler地址,软硬中断也是一样。
系统调用函数在 include/linux/syscalls.h中定义

我们拿asmlinkage long sys_openat(int dfd, const char __user *filename, int flags, umode_t mode); 来分析

这里使用了汇编链接,它和上文提到的tbl系统调用表有关。我们拿x86/i386分析,arch/x86/entry/syscalls/syscall_32.tbl
32.tbl
中断号 295 架构i386即传统32位x86 sys_openat 是其回调函数/软中断Handler

linux/include/uapi/asm-generic/unistd.h

其实现位于 arch/处理器架构/include/之下
可以在 arch/x86 下搜索 openat

关于内核的系统调用这部分,本人会在再出一个文章。

Glibc 系统调用库

注意:Glibc属于库,不属于内核,是根文件系统的一部分。
我们在应用态陷入内核态,使用的c库里的open()等等函数,最后都是链式调用到了syscall()类的系统调用函数。

看glibc的源码,就会发现弯弯绕绕,最后是调用到

作用是将参数写入寄存器,让SoC自己触发软中断,根据Linux内核注册的软中断号执行对应地址段的函数,也就是我们常在STM32里注册定义的中断的handle函数。

Linux应用态到内核态例子

在线阅读代码:

  1. https://elixir.bootlin.com/glibc/glibc-2.29/source/include/errno.h#L37
  2. https://codebrowser.dev/glibc/glibc/io/read.c.html
  3. 带了编译产物的仓库 https://github.com/bminor/glibc/tree/a81cdde1cb9d514fc8f014ddf21771c96ff2c182
    这些在线网站都不错,但为了高亮,所以我截图放了github的

我们在应用层调用 系统库的 fread()函数
其链接到glibc库的 libio/iofread.c
image

其中第44行可见其为 _IO_fread 声明了weak弱链接别名 fread,有关别名表可见编译产物如sysdeps/unix/syscalls.list
做了一些预操作之后,调用libio/libio.h 声明的 libio/genops.c:_IO_sgetn
image
宏定义 libio/libioP.h
image
ps: JUMP2代表两个参数

image
展开宏
image
展开宏
image

展开宏

image
展开宏
image

结构体
image

也就说调用了 FP.__xsgetn(FP, DATA, N) ,展开差不多是

struct _IO_FILE_plus *THIS;
THIS->vtable->__xsgetn; 即_IO_xsgetn_t类型函数指针

即THIS/file对象的函数地址 size_t __xsgetn (FILE *FP, void *DATA, size_t N);

初始化 _IO_jump_t 位于 JUMP_INIT
image

宏展开
image
函数定义
image

相关的计数器
image

再看另一个,我们常用的fopen
fopen

_IO_file_fopen

compat_symbol (libc, _IO_old_file_fopen, _IO_file_fopen, GLIBC_2_0);

_IO_old_file_fopen

#define __open open

这里定向到了 open, 我们需要通过编译产物 sysdeps/unix/syscalls.list 找到其链接段
image

可在 io/open.c 找到函数 __libc_open 位于
image
由于弱定义,所以被以下覆盖 sysdeps/unix/sysv/linux/open.c

image
关键在于第43行的SYSCALL_CANCEL,其中的宏
image
展开宏INLINE_SYSCALL_CALL
image
展开宏
image
展开宏
image
展开宏
image

展开为

//展开
__INLINE_SYSCALL4(openat, AT_FDCWD, file, oflag, mode)
//继续展开为
__INLINE_SYSCALL(openat, 4, AT_FDCWD, file, oflag, mode)

image

//x86
#define SYS_ify(syscall_name)	__NR_##syscall_name

#define INTERNAL_SYSCALL(name, nr, args...)				\
	internal_syscall##nr (SYS_ify (name), args)
//aarch64即 arm64
# define INTERNAL_SYSCALL_AARCH64(name, nr, args...)		\
	INTERNAL_SYSCALL_RAW(__ARM_NR_##name, nr, args)

x86的展开为 internal_syscall4 (__NR_openat, AT_FDCWD, file, oflag, mode)
可在 sysdeps/unix/sysv/linux/sh/arch-syscall.h 找到,其中断号为 295
image

对应openat的x86/i386中断号,刚好就是295,源码分析完全正确!每种平台的中断号都不一样,但是这样分析是正确的。
体验一下GNU宏地狱吧!

而ARM64的就高明得多,直接通过asm汇编指令写寄存器跳转执行__libc_do_syscall完成
image
glibc/sysdeps/unix/sysv/linux/arm/libc-do-syscall.S
image

总之是一个系统调用,等价于 openat(AT_FDCWD, file, oflag, mode);

总结:sysdeps是系统调用的实现,向上屏蔽细节,但是封装的过程用于一堆条件宏,根本没办法用代码分析工具,也难以调试。
对GNU LIBC代码的个人拙见:
好处:节省空间,较好的运行速度。
坏处:作为计算机世界的底层支持,这样还不如在编译器优化阶段下功夫,过多的黑魔法必然写出难以理解的代码,牵一发动全身,没有人愿意去改这堆疯狂嵌套的代码。作为新兴语言爱好者的我,始终认为程序要少点黑魔法,简洁直接才是最优解,剩下的东西都应该交给编译器,何况系统调用的耗时从来就不在这里,主要性能影响都是在内核态用户态切换的时候,并不在c库本身。

GNU的代码向来很难读,glibc更是个寄吧,各种宏和硬链接乱飞,有再好的代码阅读工具也难找出来。
但要说来,说到底还是c语言/链接器的设计缺陷,没办法更好的实现动态表和静态表。(多态组合、编译期间的函数静态段的多分支链接)

微软的代码是框架难以理解,因为他们也不给出架构图和代码结构的...,,而GNU的代码是宏和链接难以理解。

最后

请指正批评!感谢阅读。

--------蓝天上的云_转载请注明出处.