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

news2024/11/18 21:42:28

前言

最近研究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/451448.html

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

相关文章

Day3 自学Pytorch 数据集 torchvision.transforms类&torchvision.datasets.ImageFolder类

1.torchvision.transforms类 可调用的函数列表https://pytorch.org/vision/stable/transforms.html 介绍几个常用的函数&#xff1a; ① transforms.Resize(&#xff09; 将图像转换成目标大小 参数列表&#xff1a; size (sequence or int)&#xff1a; &#xff08;h,w&a…

Scala 中的 List 列表详解

目录 一、不可变长的List列表 1.List列表的声明与遍历 2.List列表的map、flatMap函数 3.List列表的filter过滤函数 4.List列表的count计数函数 二、可变长的List列表 1.可变长List声明 2.可变长List的添加方法 三、List列表其余的方法与函数 一、不可变长的List列表 …

任务调度原理 通俗讲解详细(FreeRTOS)

寄存器说明 以cortex-M3&#xff0c;首先先要了解比较特别的几个寄存器&#xff1a; r15 PC程序计数器&#xff08;Program Counter&#xff09;,存储下一条要执行的指令的地址。 r14 LR连接寄存器&#xff08;Link Register &#xff09;&#xff0c;保存函数返回地址&#x…

记忆化搜索-滑雪

题意 给定一个 R 行 C 列的矩阵&#xff0c;表示一个矩形网格滑雪场。 矩阵中第 i 行第 j 列的点表示滑雪场的第 i 行第 j 列区域的高度。 一个人从滑雪场中的某个区域内出发&#xff0c;每次可以向上下左右任意一个方向滑动一个单位距离。 当然&#xff0c;一个人能够滑动到某…

论文笔记:基于U-Net深度学习网络的地震数据断层检测

0 论文简介 论文&#xff1a;基于U-Net深度学习网络的地震数据断层检测 发表&#xff1a;2021年发表在石油地球物理勘探 1 问题分析和主要解决思路 问题&#xff1a;断层智能识别&#xff0c;就是如何利用人工智能技术识别出断层。 解决思路&#xff1a;结合&#xff35;-N…

nginx快速入门

本文应侧重操作应用&#xff0c;复杂原理详见相关理论类笔记 Nginx 快速入门笔记 Nginx 的简介 1. 什么是 nginx ​ Nginx 可以作为静态页面的 web 服务器&#xff0c;同时还支持 CGI 协议的动态语言&#xff0c;比如 perl、php等。但是不支持 java。Java 程序只能通过与 t…

tauri+rust 构建项目

文章目录 安装前依赖创建项目第一步第二步第三步最后一步 调试 昨天菜鸟尝试使用 taurirust 构建项目&#xff0c;按照网上的感觉都不是很全&#xff0c;所以这里菜鸟自己总结一下&#xff0c;主要是给自己今后学习 taurirust 使用的&#xff0c;当然也不知道会不会去学&#x…

全球医疗器械研发投入前十,这家中国公司领跑榜单

2023年&#xff0c;《医疗设计》杂志公布了最新一期百强榜&#xff0c;评选出了2022全球医疗器械行业最高研发支出和项目的十家公司。这些公司的每年研发支出超过收入的15%。尽管经济面临逆风&#xff0c;医疗器械行业的销售额却创下了新的历史高点&#xff0c;研发支出也加速增…

多通道振弦传感器无线采集仪 多类型数字传感器独立发送协议

多通道振弦传感器无线采集仪 多类型数字传感器独立发送协议 独立发送传感器数据时&#xff0c;每个传感器是一个独立的数据包&#xff0c;发送至预设的 TCP 服务器。 数据包字符串&#xff0c;结构说明如下&#xff1a; UDID>MDS传感器类型码第 x 个传感器>第 x 包/总 x …

K8s中内置的Prometheus 异常,不断重启的解决方案

要说明的一点是&#xff1a;此处理方式会进行数据的删除&#xff0c;并且多实例情况下最好都做下操作。多实例都操作一遍的意思就是比如我普罗米修斯有如下四个&#xff1a; 如果Prometheus-k8s-0一直重启&#xff0c;则不光需要操作Prometheus-k8s-0&#xff0c;也需要对它的…

VC++如何获取所有运行中的Word实例的COM对象

目录 一 问题的提出二 工程创建2.1 创建一个基于对话框的MFC工程2.2 导入word相关的自动化包装类 三 代码实例3.1 初始化COM库3.2 对话框类头文件修改3.3 对话框类实现文件1.根据进程名称获取进程ID2. 获取一个进程下所有的窗口3. 判断某个窗口是否为主窗口4. 判断word进程下面…

数字ic验证工程师经典笔试面试题(含答案)

数字ic验证工程师在找工作时&#xff0c;刷笔试面试题必不可少&#xff0c;在面试前做好充足的准备才能抓住更多的机会&#xff0c;今天小编为大家准备了数字ic验证工程师大厂面试常用笔试面试题。 下列关于代码覆盖率描述错误的是&#xff1a;CD A.代码覆盖率包括语句覆盖率…

用CD4051 实现八档位可变 / 可编程增益同相比例运放电路

CD4051 相当于一个单刀八掷的开关&#xff0c;一个公共端对应另一边八个档位&#xff0c;如下图。左边的Z 就是公共端的“单刀”&#xff0c;右边Y0 到Y7 是“八掷”&#xff0c;用A0、A1、A2 三位选择这八个档位。基于CD4051 的变增益电路实现的原理是一致的&#xff0c;只是细…

国际十大正规现货黄金交易平台排名(2023年优质版)

在现今这个时代&#xff0c;投资理财是在平常不过的了&#xff0c;但是在投资市场中黄金是最为热门的产品之一&#xff0c;而黄金投资市场中现货黄金因行情变化快、盈利机会多、多空双向交易机制而获得人们广泛喜爱和选择的主要理由&#xff0c;由于现货黄金的发展史起源于国外…

《PyTorch 深度学习实践》第12讲 循环神经网络(基础篇)

文章目录 1 什么是RNN&#xff1f;1.1 原理1.2 维度说明 2 一些琐碎代码2.1 RNNCell2.2 RNN2.3 RNN参数&#xff1a;batch_first 3 例子&#xff1a;序列变换把 "hello" --> "ohlol"3.1 使用RNNCell3.2 使用RNN3.3 使用embedding and linear layer嵌入&…

迅为iTOP4412精英版Visual Studio Code 插件安装

我们在此以 ubuntu 环境为例&#xff0c;讲解 Visual Studio Code 插件安装。 VSCode 支持多种语言&#xff0c;比如 C/C、Python、C#等等&#xff0c;对于嵌入式开发的我们主要用来编写 C/C程 序的&#xff0c;所以需要安装 C/C的扩展包&#xff0c;扩展包安装很简单&#x…

【 SpringBoot单元测试 和 Mybatis 增,删,改 操作 】

文章目录 一、Spring-Boot单元测试(了解)1.1 概念1.2 单元测试引用1.3 单元测试的实现1.4 简单的断言说明1.5 单元测试优点 二、Mybatis 增&#xff0c;删&#xff0c;改 操作2.1 增加⽤户操作2.2 修改⽤户操作2.3 删除⽤户操作 一、Spring-Boot单元测试(了解) 1.1 概念 单元测…

Java设计模式-day02

4&#xff0c;创建型模式 4.2 工厂模式 4.2.1 概述 需求&#xff1a;设计一个咖啡店点餐系统。 设计一个咖啡类&#xff08;Coffee&#xff09;&#xff0c;并定义其两个子类&#xff08;美式咖啡【AmericanCoffee】和拿铁咖啡【LatteCoffee】&#xff09;&#xff1b;再设…

找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll

找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll 找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll 找不到msvcp140d.dll vcruntime140d.dll ucrtbased.dll 链接&#xff1a;https://pan.baidu.com/s/15O9cRwexHp4nzZj8eYVfnw 提取码&#xff1a;4iyr --来自百度…

FPGA基于XDMA实现PCIE X8的HDMI视频采集 提供工程源码和QT上位机程序和技术支持

目录 1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案5、vivado工程详解6、驱动安装7、QT上位机软件8、上板调试验证9、福利&#xff1a;工程代码的获取 1、前言 PCIE&#xff08;PCI Express&#xff09;采用了目前业内流行的点对点串行连接&#xff0c;比起 PC…