无法从C中的共享库返回正确的内存地址



我一直在尝试实现一个小型模拟,以了解malloc((的内存分配。我创建了一个名为mem.c的共享库;堆";。堆是由共享库中的malloc((调用创建的。

共享库中的地址:0x55ddaff662a0主地址:0xffffffffaff662a0

似乎只有最后4个字节是正确的。Rest设置为0xf。

然而,当我#包括";mem.c";总的来说,它工作正常。我如何在不包括mem.c的情况下实现相同的结果。我试图在不包括mem.c或mem.h的情况下解决这个问题。我创建共享库如下:

gcc -c -fpic mem.c
gcc -shared -o libmem.so mem.o
gcc main.c -lmem -L. -o main

根据您的评论

我试图在不使用#includemem.h或mem.c的情况下实现

然后您必须通过其他方式为您正在调用的函数提供原型。遵循K&R和后来的ANSI C,未声明的函数被假定返回int并接受类型为int的参数。

EDIT:本质上,在首次使用该函数之前,您需要在某个地方写下通常在页眉中找到的内容。或者它是一个函数指针,你需要一个合适的变量来存储函数指针

例如,为了声明一个返回非类型化指针和任意数量的未指定参数的函数,您可以编写

void *getAddr();

请注意,这里不需要使用extern关键字,因为非静态函数声明总是隐含外部链接。

如果您想在运行时动态链接(使用dlopen/LoadLibrarydlsym/GetProcAddress(,您可以定义一个函数指针变量

void* (*getAddr_fptr)();

您可以使用带有的dlsym进行设置

*(void**)(&getAddr_fptr) = dlsym(…)

这种尴尬的编写方式是由于函数指针被允许与数据指针具有不同的大小和对齐方式(有关详细信息,请参阅dlsym手册页(。

目前,在大多数平台上,int是一种4字节类型,最常见的调用约定是通过寄存器传递前几个函数参数。在x86(和x86_64(上,寄存器是AX、BX、CX和DX,可以不同大小访问,但可以不同大小读取和写入(以允许大小转换(。这就解释了为什么只传递前4个字节:它是通过寄存器传递的,只有对寄存器的写入才是4字节宽的写入。当函数从寄存器中读取时,它会使用更宽的类型进行读取,其中较高值的位设置为全部1。

来自注释:

您的主代码中有getAddr的声明吗?

不,我没有,但我正在尝试在没有声明的情况下实现,这可能吗?

那就是你的问题了。如果没有声明,编译器将返回到默认声明int getAddr()。这与返回void *的实际定义不兼容,通过不兼容的声明调用函数会触发未定义的行为。

可能发生的情况是,当函数的返回值实际返回时,您只返回了4个低位字节。假设您的系统是小端序,int是4个字节,void *是8个字节,这可以解释低位是相同的。

在调用函数之前,必须包含有效声明。它不一定必须位于头文件中,但必须在调用时可见。

我假设你正试图完成这样的事情?对于mem.c

#include <stdlib.h>
#include <stdio.h>
void* getAddr() {
char *heap = (char *)malloc(10);
printf("%pn", (void*)heap);
return heap;
}

然后,在不包含mem.c函数的任何头的情况下,您可能会根据问题中已经提到的mem.c创建一个库,并在main.c中获得如下内容

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
typedef void* (*getAddr)(); //prototype for getAddr() in mem.c
int main() {
void* handle = dlopen("./libmem.so", RTLD_LAZY);
if(handle) {
void* fn = dlsym(handle, "getAddr");
if(fn) {
void* addr = ((getAddr)(fn))();
printf("%pn", addr);
free(addr);
addr = NULL;
} else {
printf("Failed to dlsym %sn", dlerror());
}
} else {
printf("Failed to dlopen %sn", dlerror());
}
}

编辑:对于@Zilog80提到的OP的目的,由于库与main可执行文件链接,因此可以去掉dlopen()部分,main.c可以简化为

#include <stdio.h>
#include <stdlib.h>
extern void* getAddr(); //prototype for getAddr() in mem.c
int main() {
void* addr = getAddr();
printf("%pn", addr);
free(addr);
addr = NULL;
}

并使用了与OP类似的编译命令,即

gcc -shared -o libmem.so -fpic mem.c
gcc main.c -lmem -L . -o main

执行时

LD_LIBRARY_PATH=. ./main

最新更新