改变函数指针的地址值

Changing the address value of a function pointer

我在 C 中有以下代码:

int addInt(int n, int m) {
    return n + m;
}

int (*functionPtr)(int, int);

functionPtr = &addInt;

functionPtr是一个函数指针,指向函数addInt的具体地址。我想更改其值的 1 位,但我不知道如何更改。

假设 functionPtr 在最后一条语句之后指向 0xABC0(假设一个 16 位地址)。我想将其值更改为 0xABC1。我尝试将值与 0x1 进行或运算,但我猜操作数转换有问题:

functionPtr = &addInt | 0x00000001; // addresses are of 32 bits

我知道乱用指针是有风险的,但我必须更改地址的 LSB 才能进入 ARM Cortex-M4 MCU 的 Thumb 状态。

这可能是个坏主意,但如果您真的需要这个,以下方法可以解决问题:

functionPtr = &addInt;
*(int*)&functionPtr |= 1;

但请注意,这不可移植,在您的环境中可能有效或根本无效。

要通过算术运算修改指针的值,您需要将其转换为整数类型,执行运算,然后再转换回来。但是,C 没有定义将函数指针转换为另一个函数指针以外的任何东西的行为,因此没有定义的方法来做到这一点。

你仍然可以这样写:

typedef int (*fptr)(int, int);

functionPtr = (fptr)(((intptr_t) functionPtr) | 1);

您正在寻找的行为是比较合理的结果之一,但同样,两个转换的行为是 未定义。因此,通过修改后的指针执行函数调用所预期的行为——如果您的程序甚至可以做到这一点——也是未定义的。编译器甚至不需要接受代码。

函数指针格式取决于体系结构和实现。

例如,在 Itanium 上,function pointers point to a read only data structure 包含函数所属模块的适当 全局指针 ,以及实际函数指针。

很多人提到你的方法很危险,但在某些情况下,这是一个真实的问题,我不止一次遇到过。我怀疑目前您的代码使用 BL 指令(分支和 link),它不会将 CPU 的模式更改为拇指,以便执行从 ARM 到thumb 基于你的函数指针,在编译代码时将 -mthumb-interwork 标志添加到 gcc:

Generate code that supports calling between the ARM and Thumb instruction sets. Without this option, on pre-v5 architectures, the two instruction sets cannot be reliably used inside one program. The default is -mno-thumb-interwork, since slightly larger code is generated when -mthumb-interwork is specified. In AAPCS configurations this option is meaningless.