为什么在C上对动态链接符号执行指针算术时会出现错误结果?
我遇到了一个奇怪的情况,其中执行指针算术涉及 动态链接的符号导致不正确的结果。我不确定 是否仅仅缺少一些链接器参数,或者它是链接器错误。有人可以在下面的例子中解释有什么不对吗?为什么在C上对动态链接符号执行指针算术时会出现错误结果?
考虑一个简单的共享库的下面的代码(lib.c
):
#include <inttypes.h>
#include <stdio.h>
uintptr_t getmask()
{
return 0xffffffff;
}
int fn1()
{
return 42;
}
void fn2()
{
uintptr_t mask;
uintptr_t p;
mask = getmask();
p = (uintptr_t)fn1 & mask;
printf("mask: %08x\n", mask);
printf("fn1: %p\n", fn1);
printf("p: %08x\n", p);
}
有问题的操作的位与fn1
和 可变mask
地址之间。应用程序(app.c
)只是调用fn2
这样的:
extern int fn2();
int main()
{
fn2();
return 0;
}
它导致下面的输出...
mask: ffffffff
fn1: 0x2aab43c0
p: 000003c0
...这显然是不正确的,因为相同的结果将预计fn1
和p
。代码运行上的AVR32架构和如下被编译:
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -c -o lib.o lib.c
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -shared -o libfoo.so lib.o
$ avr32-linux-uclibc-gcc -Os -Wextra -Wall -o app app.c -L. -lfoo
编译器认为,它是将可变 mask
加载到32位寄存器7的最优解和分裂& -operation成两个汇编 立即操作数的操作。
$ avr32-linux-uclibc-objdump -d libfoo.so
000003ce <fn1>:
3ce: 32 ac mov r12,42
3d0: 5e fc retal r12
000003d2 <fn2>:
...
3f0: e4 17 00 00 andh r7,0x0
3f4: e0 17 03 ce andl r7,0x3ce
我承担的and
指令立即数不搬迁 到fn1
加载地址时,共享库被加载到 应用程序的地址空间:
- 这种行为是故意的吗?
- 如何调查链接共享库时或加载可执行文件时是否出现问题?
背景:这不是一个学术问题。 OpenSSL和LibreSSL 使用类似的代码,所以更改C源不是一个选项。该代码在其他体系结构上运行的代码为 ,当然, 对函数指针执行按位运算的确有一个不明显的原因。
后的代码校正所有的 'slopiness',其结果是:
#include <inttypes.h>
#include <stdio.h>
int fn1(void);
void fn2(void);
uintptr_t getmask(void);
int main(void)
{
fn2();
return 0;
}
uintptr_t getmask()
{
return 0xffffffff;
}
int fn1()
{
return 42;
}
void fn2()
{
uintptr_t mask;
uintptr_t p;
mask = getmask();
p = (uintptr_t)fn1 & mask;
printf("mask: %08x\n", (unsigned int)mask);
printf("fn1: %p\n", fn1);
printf("p: %08x\n", (unsigned int)p);
}
和输出(我的Linux 64位计算机上)是:
mask: ffffffff
fn1: 0x4007c1
p: 004007c1
'%p'格式说明符必须采用'void *'参数 –
@ M.M:否则会导致UB。 – Destructor
因为任何指针都可以作为参数列表中的'void *'使用,所以编译器不会抱怨语句:'printf(“fn1:%p \ n”,fn1);'并且会产生正确的结果。 – user3629249
'返回0xffffffff' - > 'return〜(uintptr_t)0'? – Bathsheba
什么'print(“fn1-x:%08x \ n”,(uintptr_t)fn1);'给? –
如果你没有优化编译,你会得到什么? –