RISC-V平台libc里面的setjmp和longjmp实现

libc从版本2.0开始,增加了对setjmp和longjmp的支持,这2个函数是实现协程的核心函数。

我们来看一下RISC-V平台上这两个函数的底层是如何实现的。

libc源码:https://sourceware.org/git/?p=glibc.git;a=summary

https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/riscv/setjmp.S;h=0b92016b311b11aa9eeb62b38c670a262f1924c9;hb=1d9764aba8c00754fbf8299e48afbe222245ee3e

 

​git clone https://sourceware.org/git/glibc.git

setjmp/setjmp.h定义两个函数的原型

setjmp(jmp_buf __env);

longjmp(struct __jmp_buf env, int _val);

这两个函数的介绍可以参见:温州皮鞋的博客彻底理解setjmp/longjmp并DIY一个简单的协程_Netfilter,iptables/OpenVPN/TCP guard:-(-CSDN博客_longjmp上海昔日的邻居来杭州,我们小聚。今天一起带着小小去了西溪湿地,体验并不是太好,门票太贵,进去需乘船,船票快顶得上门票了,简直就是要抢劫啊!不过,景点嘛,一般我是不去的。缘由下面的代码写于5月2日(也就是今天刚开始的时候)凌晨1点钟,假期首日逛玩了一天,晚上自己写个东西玩玩,主要是因为之前想用setjmp/longjmp通过修改jmp_buf结构体的某些寄存器字段而实现协程而没有成功,就自己做个...https://blog.csdn.net/dog250/article/details/89742140

typedef struct __jmp_buf_tag jmp_buf[1];

/* Store the calling environment in ENV, also saving the signal mask.
   Return 0.  */
extern int setjmp (jmp_buf __env) __THROWNL;

/* Jump to the environment saved in ENV, making the
   `setjmp' call there return VAL, or 1 if VAL is 0.  */
extern void longjmp (struct __jmp_buf_tag __env[1], int __val)
     __THROWNL __attribute__ ((__noreturn__));

#if defined __USE_MISC || defined __USE_XOPEN
/* Same.  Usually `_longjmp' is used with `_setjmp', which does not save
   the signal mask.  But it is how ENV was saved that determines whether
   `longjmp' restores the mask; `_longjmp' is just an alias.  */
extern void _longjmp (struct __jmp_buf_tag __env[1], int __val)
     __THROWNL __attribute__ ((__noreturn__));
#endif



typedef struct __jmp_buf_tag sigjmp_buf[1];

riscv平台的具体实现位于目录:sysdeps/unix/sysv/linux/riscv/

setjmp.h头文件,定义了__jmp_buf类型的结构体。

bits/setjmp.h

#ifndef _RISCV_BITS_SETJMP_H
#define _RISCV_BITS_SETJMP_H

typedef struct __jmp_buf_internal_tag
  {
    /* Program counter.  */
    long int __pc;
    /* Callee-saved registers.  */
    long int __regs[12];
    /* Stack pointer.  */
    long int __sp;

    /* Callee-saved floating point registers.  */
#if defined __riscv_float_abi_double
   double __fpregs[12];
#elif !defined __riscv_float_abi_soft
# error unsupported FLEN
#endif
  } __jmp_buf[1];

#endif /* _RISCV_BITS_SETJMP_H */

汇编代码如下:

setjmp.S 保存寄存器到内存中,a0中保存的是setjmp(env) 函数传递过来的_env, _env的是__jmp_buf类型的结构体,其中有变量用于保存寄存器。

#include <sysdep.h>
#include <sys/asm.h>

ENTRY (_setjmp)
  li	a1, 0
  j	HIDDEN_JUMPTARGET (__sigsetjmp)
END (_setjmp)
ENTRY (setjmp)
  li	a1, 1
  /* Fallthrough */
END (setjmp)
ENTRY (__sigsetjmp)
	REG_S ra,  0*SZREG(a0)
	REG_S s0,  1*SZREG(a0)
	REG_S s1,  2*SZREG(a0)
	REG_S s2,  3*SZREG(a0)
	REG_S s3,  4*SZREG(a0)
	REG_S s4,  5*SZREG(a0)
	REG_S s5,  6*SZREG(a0)
	REG_S s6,  7*SZREG(a0)
	REG_S s7,  8*SZREG(a0)
	REG_S s8,  9*SZREG(a0)
	REG_S s9, 10*SZREG(a0)
	REG_S s10,11*SZREG(a0)
	REG_S s11,12*SZREG(a0)
	REG_S sp, 13*SZREG(a0)

#ifndef __riscv_float_abi_soft
	FREG_S fs0, 14*SZREG+ 0*SZFREG(a0)
	FREG_S fs1, 14*SZREG+ 1*SZFREG(a0)
	FREG_S fs2, 14*SZREG+ 2*SZFREG(a0)
	FREG_S fs3, 14*SZREG+ 3*SZFREG(a0)
	FREG_S fs4, 14*SZREG+ 4*SZFREG(a0)
	FREG_S fs5, 14*SZREG+ 5*SZFREG(a0)
	FREG_S fs6, 14*SZREG+ 6*SZFREG(a0)
	FREG_S fs7, 14*SZREG+ 7*SZFREG(a0)
	FREG_S fs8, 14*SZREG+ 8*SZFREG(a0)
	FREG_S fs9, 14*SZREG+ 9*SZFREG(a0)
	FREG_S fs10,14*SZREG+10*SZFREG(a0)
	FREG_S fs11,14*SZREG+11*SZFREG(a0)
#endif

#if !IS_IN (libc) && IS_IN (rtld)
  /* In ld.so we never save the signal mask.  */
  li a0, 0
  ret
#else
  /* Make a tail call to __sigjmp_save; it takes the same args.  */
  j __sigjmp_save
#endif


END (__sigsetjmp)

hidden_def (__sigsetjmp)
weak_alias (_setjmp, __GI__setjmp)

__longjmp.S 从内存中恢复寄存器


#include <sysdep.h>
#include <sys/asm.h>

ENTRY (__longjmp)
	REG_L ra,  0*SZREG(a0)
	REG_L s0,  1*SZREG(a0)
	REG_L s1,  2*SZREG(a0)
	REG_L s2,  3*SZREG(a0)
	REG_L s3,  4*SZREG(a0)
	REG_L s4,  5*SZREG(a0)
	REG_L s5,  6*SZREG(a0)
	REG_L s6,  7*SZREG(a0)
	REG_L s7,  8*SZREG(a0)
	REG_L s8,  9*SZREG(a0)
	REG_L s9, 10*SZREG(a0)
	REG_L s10,11*SZREG(a0)
	REG_L s11,12*SZREG(a0)
	REG_L sp, 13*SZREG(a0)

#ifndef __riscv_float_abi_soft
	FREG_L fs0, 14*SZREG+ 0*SZFREG(a0)
	FREG_L fs1, 14*SZREG+ 1*SZFREG(a0)
	FREG_L fs2, 14*SZREG+ 2*SZFREG(a0)
	FREG_L fs3, 14*SZREG+ 3*SZFREG(a0)
	FREG_L fs4, 14*SZREG+ 4*SZFREG(a0)
	FREG_L fs5, 14*SZREG+ 5*SZFREG(a0)
	FREG_L fs6, 14*SZREG+ 6*SZFREG(a0)
	FREG_L fs7, 14*SZREG+ 7*SZFREG(a0)
	FREG_L fs8, 14*SZREG+ 8*SZFREG(a0)
	FREG_L fs9, 14*SZREG+ 9*SZFREG(a0)
	FREG_L fs10,14*SZREG+10*SZFREG(a0)
	FREG_L fs11,14*SZREG+11*SZFREG(a0)
#endif

	seqz a0, a1
	add  a0, a0, a1   # a0 = (a1 == 0) ? 1 : a1
	ret

END (__longjmp)

sys/asm.h

定义一些辅助性的宏定义、ENTRY 这些

#ifndef _SYS_ASM_H
#define _SYS_ASM_H

/* Macros to handle different pointer/register sizes for 32/64-bit code.  */
#if __riscv_xlen == 64
# define PTRLOG 3
# define SZREG  8
# define REG_S sd
# define REG_L ld
#elif __riscv_xlen == 32
# define PTRLOG 2
# define SZREG  4
# define REG_S sw
# define REG_L lw
#else
# error __riscv_xlen must equal 32 or 64
#endif

#if !defined __riscv_float_abi_soft
/* For ABI uniformity, reserve 8 bytes for floats, even if double-precision
   floating-point is not supported in hardware.  */
# if defined __riscv_float_abi_double
#  define FREG_L fld
#  define FREG_S fsd
#  define SZFREG 8
# else
#  error unsupported FLEN
# endif
#endif

/* Declare leaf routine.  */
#define	LEAF(symbol)				\
		.globl	symbol;			\
		.align	2;			\
		.type	symbol,@function;	\
symbol:						\
		cfi_startproc;

/* Mark end of function.  */
#undef END
#define END(function)				\
		cfi_endproc;			\
		.size	function,.-function

/* Stack alignment.  */
#define ALMASK	~15

#endif /* sys/asm.h */

risc-v的寄存器参见:5.4 RISC-V寄存器 - 知乎

后面标着Callee的寄存器就是上面需要保存和恢复的寄存器。

参考:

risc-v汇编指令编写指南: https://shakti.org.in/docs/risc-v-asm-manual.pdf

risc-v汇编手册: riscv-asm-manual/riscv-asm.md at master · riscv-non-isa/riscv-asm-manual · GitHub

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路边闲人2

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值