C语言 非本地跳转实现native层TryCatch

news2024/10/3 3:27:36

前言

最近研究native hook的技术,了解到了这个非本地跳转,本文就是介绍他,对于解决native crash非常有用。

非本地跳转介绍

C语言的本地跳转是指goto、break、continue等语句,但是这个语句最大局限就是只能实现函数内部的跳转。
C 语言提供了一种用户级异常控制流形式,称为非本地跳转(non local jump),它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用—返回序列。
非本地跳转是通过 setjmp 和 longjmp 等成对的函数来提供的,siglongjmp() is similar to longjmp(), except for the optional capability of restoring the signal mask. The sigsetjmp()—siglongjmp() pair, the setjmp()—longjmp() pair, the _setjmp()—_longjmp() pair, and the getcontext()—setcontext() pair cannot be intermixed.

setjmp和 longjmp使用

setjmp函数主要用来保存当前执行状态,作为后续跳转的目标。调用setjmp时,当前状态会被存放在env指向的结构中,这个env将被 long_jmp 操作作为参数,以返回调用点,跳转的结果看起来就好像刚从setjmp返回一样,所以这个保存状态的jmp_buf变量env一般定义为全局变量。
setjmp第一次调用setjmp的时候返回值为0;而从long_jmp操作返回时,返回值是由longjmp传入的参数value决定。通过判断setjmp的返回值,就可以判断当前返回的状态。

jmp_buf jmpbuffer;

static void print_0(int i) {
    printf("print_0 start : i = %d\n", i);
    i += 10;
    printf("print_0 end : i = %d\n", i);

    longjmp(jmpbuffer, 1);
    printf("print_0 return");
}

static void print_1(int i) {
    printf("print_1 start : i = %d\n", i);
    i += 11;
    printf("print_1 end : i = %d\n", i);

    longjmp(jmpbuffer, 2);
    printf("print_1 return");
}

static void print_2(int i) {
    printf("print_2 start : i = %d\n", i);
    i += 12;
    printf("print_2 end : i = %d\n", i);

    longjmp(jmpbuffer, 3);
    printf("print_2 return");
}

static void print_3(int i) {
    printf("print_3 start : i = %d\n", i);
    i += 13;
    printf("print_3 end : i = %d\n", i);

    printf("print_3 return\n");
}

void testJump() {
    int i = 0;
    int flag;

    int start = av_gettime();
    flag = setjmp(jmpbuffer);
    int end = av_gettime();
    printf("cost %d\n", end - start);

    if (flag == 0) {
        print_0(i);
    } else if (flag == 1) {
        print_1(i);
    } else if ((flag == 2)) {
        print_2(i);
    } else {
        print_3(i);
    }
}

sigaction函数介绍

#include<signal.h>

struct sigaction {
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
};

int sigaction(int sig, const struct sigaction *act, struct sigaction *old_act);

sigaction会依参数signum指定的信号编号来设置该信号的处理函数。
参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。

  • sigaction结构体
    信号处理函数可以采用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。
    到底采用哪个要看sa_flags中是否设置了SA_SIGINFO位,如果设置了就采用void (*sa_sigaction)(int, siginfo_t *, void *),此时可以向处理函数发送附加信息;默认情况下采用void (*sa_handler)(int),此时只能向处理函数发送信号的数值。
    sa_handler:此参数和signal()的参数handler相同,代表新的信号处理函数,其他意义请参考signal();
    sa_mask:用来设置在处理该信号时暂时将sa_mask指定的信号集搁置;
    sa_restorer:此参数没有使用;
    sa_flags :用来设置信号处理的其他相关操作,下列的数值可用。sa_flags还可以设置其他标志:

    • SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
    • SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
    • SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。
    • SA_SIGINFO:在接收到信号时将额外信息传递给信号处理程序。
  • 常见的signal信号
    image.png

sigsetjmp和siglongjmp介绍

#include <setjmp.h>

int sigsetjmp(sigjmp_buf env, int savemask);
void siglongjmp(sigjmp_buf env, int val);
  • siglongjmp() is similar to longjmp(), except for the optional capability of restoring the signal mask. The sigsetjmp()—siglongjmp() pair, the setjmp()—longjmp() pair, the _setjmp()—_longjmp() pair, and the getcontext()—setcontext() pair cannot be intermixed.
  • A stack environment and signal mask saved by sigsetjmp() can be restored only by siglongjmp().
  • The stack environment and signal mask saved by sigsetjmp() can subsequently be restored by siglongjmp(). these functions provide a way to perform a nonlocal goto.
  • For a stack environment previously saved in env by sigsetjmp(), the siglongjmp() function restores all the stack environment and, optionally, the signal mask, depending on whether it was saved by sigsetjmp(). The sigsetjmp() and siglongjmp() functions provide a way to perform a nonlocal goto.

sigsetjmp

Saves the current stack environment including, optionally, the current signal mask.
A call to sigsetjmp() causes it to save the current stack environment in env.

  • env is an address for a sigjmp_buf structure.

  • savemask is a flag used to determine if the signal mask is to be saved.
    If the value of the savemask parameter is nonzero, it will also save the current signal mask in env.
    If it has a value of 0, the current signal mask is not to be saved or restored as part of the environment. Any other value means the current signal mask is saved and restored.

  • Returned value
    sigsetjmp() returns 0 when it is invoked to save the stack environment and signal mask.

siglongjmp

A subsequent call to siglongjmp() restores the saved environment and signal mask (if saved by sigsetjmp()), and returns control to a point corresponding to the sigsetjmp() call.

  • env is an address for a sigjmp_buf structure.
  • val is the return value from siglongjmp().

实现native层 try catch

static sigjmp_buf env;

static void sa_handler(int sig) {
    LOG("sa_handler: %d, 捕获到异常信号,执行跳转", sig);
    if (sig == SIGSEGV) {
        siglongjmp(env, 1);  // 捕获到异常了,跳转到jmp地址,参数为1
    }
}

void testNativeCrash() {
    auto clock_start = clock();
    int ret = sigsetjmp(env, 1);  // 设置setjmp
    auto clock_end = clock();
    LOG("sigsetjmp cost: %ld", clock_end - clock_start);

    if (ret == 0) {
        LOG("sigsetjmp return 0, 调用成功");
        raise(SIGSEGV); // 抛出一个异常
    } else {
        LOG("sigsetjmp return %d, catch住异常了", ret); // 捕获到异常
    }
}


void initSigaction() {
    struct sigaction action{};
    action.sa_flags = SA_SIGINFO;
    action.sa_handler = sa_handler;   // 注册信号处理函数
    sigemptyset(&action.sa_mask);
    sigaction(SIGSEGV, &action, nullptr);
}

sigaction
Android SIGQUIT(3) 信号拦截与处理
SIGSEGV的段错误保护实现(基于sigsetjmp)
sigsetjmp
siglongjmp
C语言——非本地跳转

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/436564.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习 - 40. N-Gram 采样与 Session 数据获取 For EGES

目录 一.引言 二.订单数据预处理 1.数据样例 2.订单数据处理 3.用户 Session 构建 三.构造 sku_id 游走序列 1.获取完整 Session List 2.统计 sku_id 转移词频 3.构建 sku_id 图 4.游走构造 sku 序列 四.商品侧信息预处理 1.读取商品信息 2.Left Join 匹配侧信息 …

三甲医院体检管理系统源码PEIS ,体检总检建议、体检套餐、各种模板

PEIS体检管理系统源码 本套PEIS医院体检管理系统源码&#xff0c;采用C#语言开发&#xff0c;C/S架构&#xff0c;前台开发工具为Vs2012&#xff0c;后台数据库采用oracle大型数据库。有演示。 文末获取联系 PEIS体检管理系统适用于大中型医院的独立体检中心、干部保健处、民营…

Java基础:编译时和运行时的区别

在java开发设计过程中&#xff0c;了解java运行时和编译时的区别非常有必要。 如下从几个问题来描述两者的区别 1、如下代码片段中&#xff0c;A行和B行的区别是什么 line A是在编译时计算值&#xff0c;line B是在运行时计算值。 当该类编译后&#xff0c;如果使用一些反编译…

ROS导航

参考文章&#xff1a; (31条消息) ROS导航小车1 teb_local_planner参数(仅作记录/收集)_teb local planner参数_Crush Mome的博客-CSDN博客 运行导航&#xff1a; 1. 启动底盘控制包 &#xff1a; base_conctronl 2.启动键盘控制节点&#xff1a; rosrun teleop_twist_ke…

ROG魔霸7Plus电脑一直蓝屏错误怎么重装系统?

ROG魔霸7Plus电脑一直蓝屏错误怎么重装系统&#xff1f;有用户在使用ROG魔霸7Plus电脑的时候&#xff0c;频繁的遇到了系统蓝屏的情况。因为这样影响了自己的正常使用&#xff0c;所以想要进行系统的重新安装。那么如何进行系统重装操作呢&#xff1f;来看看以下的操作方法教学…

ONES × 中国信通院《中国企业软件研发管理白皮书》即将发布 | 预约直播

由 ONES 与中国信息通信研究院联合发起的《中国企业软件研发管理白皮书》&#xff08;下称「白皮书」&#xff09;将于 4 月 20 日 正式发布。白皮书由 ONES、中国信息通信研究院云计算与大数据研究所、招商基金管理有限公司、紫金财产保险股份有限公司、深圳市鸿合创新信息技术…

AI大模型内卷加剧,商汤凭什么卷进来

2023年&#xff0c;国内大模型何其多。 目前&#xff0c;已宣布推出或即将推出大模型的国内企业多达20余家&#xff0c;基本上能想到的相关企业都已入局。其中&#xff0c;既有资金雄厚的BAT、华为、字节等大厂&#xff0c;也有王慧文、王小川、周伯文等互联网大佬领衔的初创企…

外卖小程序01

目录 nginx反向代理和负载均衡反向代理好处nginx反向代理的配置方式 负载均衡**nginx 负载均衡的配置方式&#xff1a;****nginx 负载均衡策略&#xff1a;** 动静分离 用户密码加密需求代码实现 Swagger框架介绍使用步骤常用注解使用案例:员工登录EmployeeController实体类Emp…

科研成果 | 不同调制方式的开源数据集及其数据扩增方式

文章目录 1. 数据源2. 数据扩增2.1 基于opencv的一些基础变换2.2 基于GAN网络的方法2.2.1 SinGAN2.2.2 基于多图的GAN方法1. 数据源 网址: https://www.sigidwiki.com/wiki/VHF 每种数据基本只有一条,所以要用的话只能进行数据扩征 2. 数据扩增 两种方法: 基于opencv的一…

浏览器输入 http 自动转 https 问题解决方法

目录 表象 原因 解决方案 解决方案一 解决方案二 表象 今天在开发的过程中遇到一个问题&#xff0c;我们项目的地址是 “http://xxx.xxx.com/website/” &#xff0c;结果粘贴到浏览器里自动跳转成了 “https://xxx.xxx.com/website/”。百思不解啊&#xff0c;为啥呢。 …

git上如何通过本地仓库推送自己的代码到远程仓储

从gitHub或者gitee上拉取代码后&#xff0c;我老是想着把别人的代码保存到自己的仓库上&#xff0c;这里教你一招。 gitee的&#xff1a; 首先我们在gitee或者github上创建一个自己的仓库&#xff0c;github的我就不在展示了&#xff0c;基本上和gitee操作一样 输入相关信息…

【redis】bitmap、hyperloglog、GEO案例

【redis】bitmap、hyperloglog、GEO案例 文章目录 【redis】bitmap、hyperloglog、GEO案例前言一、面试题二、统计的类型聚合统计排序统计问题&#xff1a;思路 二值统计 0和1基数统计 三、hyperloglog1、名词理解UV 独立访客PV 页面浏览量DAU 日活跃用户MAU 月活跃度 2、看需求…

愚蠢的往事-网络安全专题之数字证书

血泪教训史&#xff0c;我被骗去办理了数字证书。 文章目录 加密算法摘要算法数字签名数字证书血泪开篇数字证书 加密算法 相关知识点&#xff1a;对称加密算法、非对称加密算法、信息完整性验证算法。 对称加密算法&#xff1a;1.加密密钥和解密密钥相同的算法&#xff0c;…

小行助学答题系统编程等级考试scratch三级真题2023年3月(含题库答题软件账号)

青少年编程等级考试scratch真题答题考试系统请点击 电子学会-全国青少年编程等级考试真题Scratch一级&#xff08;2019年3月&#xff09;在线答题_程序猿下山的博客-CSDN博客_小航答题助手 1.计算“248……128”&#xff0c;用变量n表示每项&#xff0c;根据变化规律&#xf…

SpringCloud+RabbitMQ+Docker+Redis+搜索+分布式,系统详解springcloud微服务技术栈

http客户端Feign &#xff08;一&#xff09;基于Feign远程调用 1、RestTemplate方式调用存在的问题 2、Feign的介绍 3、定义和使用Feign客户端 这个接口里面将来的封装的就是所有对userservice发起的远程调用 1、orderserivce的pom <!--feign客户端依赖--> <depe…

Dsq: 用于针对JSON、CSV、Excel、Parquet等运行SQL查询的命令行工具

目录 About Install macOS Homebrew macOS、Linux和WSL上的二进制文件 Windows上的二进制文件&#xff08;非WSL&#xff09; 从源代码生成和安装 Usage Pretty print dsq的管道数据 多个文件和连接 无需查询即可将数据转换为JSON 嵌套在对象中的对象数组 嵌套对象…

ASRock Z690 Extreme WiFi 6E i7 13700KF电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件型号驱动情况 主板ASRock Z690 Extreme WiFi 6E 处理器Intel Core i7 13700KF已驱动 内存KINGBANK 2x32GB DDR4-3600CL18已驱动 硬盘Predator SSD GM7000 1TB已驱动…

qt5.15.2配置android

qt安装安卓编译器就直接跳过&#xff0c;我们开始将如何进行配置。 如果专门开发的app&#xff0c;则应该使用android进行开发&#xff0c;qt是熟悉qt语言&#xff0c;或者app需要进行跨平台的话则使用qt for android比较好。 下载 首先安装jdk&#xff0c;最好安装 jdk11&am…

[Java] Socket (UDP , TCP)

目录 什么是Socket ? TCP api 与 UDP api 的特点 : UDP api 使用UDP Socket 实现一个单词翻译 : TCP api 使用TCP协议来实现一个回显服务 什么是Socket ? 应用层和传输层之间的桥梁 . 程序猿写网络代码 (应用层) , 要想发送这个数据 , 就需要去调用下层协议 , 应用层…

uniapp图片转base64及JS各文件类型相互转换

uniapp图片转base64及JS各文件类型相互转换 1、chooseImage request arrayBufferToBase642、chooseImage getFileSystemManager3、chooseImage FileReader4、扩展-JS各文件类型相互转换4.1 File 转成 ArrayBuffer4.2 File 转成 blob4.3 File 转成 base644.4 ArrayBuffer 转…