You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何直接链接与dlopen加载同一库时函数内存地址不同?

Why sqrt Has Different Addresses When Linked Directly vs. dlopen-ed

Great question! This behavior boils down to how ELF dynamic linking works on Linux, specifically the PLT (Procedure Linkage Table) and GOT (Global Offset Table) mechanism used for direct dynamic linking, versus direct symbol lookup via dlopen/dlsym.

First, let's recap your test setup to make sure we're on the same page:

Your Test Code & Output

You compiled a program that both links libm.so.6 directly (with -lm) and loads it dynamically via dlopen:

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <math.h>
#include <inttypes.h>
int main() {
    void *dl, *dl_sqrt;
    dl = dlopen("/lib/x86_64-linux-gnu/libm.so.6", RTLD_LAZY);
    if (!dl) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }
    dl_sqrt = dlsym(dl,"sqrt");
    if (!dl_sqrt) {
        fprintf(stderr, "%s\n", dlerror());
        exit(1);
    }
    printf("Address of sqrt %p\n", (void*) sqrt);
    printf("Address of (dl)sqrt %p\n", (void*) dl_sqrt);
    return 0;
}

Compilation command:

gcc dl-test.c -lm -ldl -o dl-test

Running it gives:

Address of sqrt 0x4006d0
Address of (dl)sqrt 0x7fa7ce6f7250

Why the Addresses Differ

1. The Directly Linked sqrt Address: A PLT Stub

When you link libm directly with -lm, the sqrt symbol in your code doesn't point directly to the actual sqrt function in libm.so.6. Instead, it points to a small stub function in your program's PLT (Procedure Linkage Table).

The PLT is a section in your executable that exists to handle lazy dynamic binding (the RTLD_LAZY behavior you're using). Here's how it works:

  • On the first call to sqrt(), the PLT stub checks the GOT (Global Offset Table) for the real address of sqrt.
  • Initially, the GOT entry points back to the PLT stub, which triggers the dynamic linker (ld-linux-x86-64.so.2) to look up sqrt in libm.so.6, resolve its real address, and write that address to the GOT entry.
  • Subsequent calls to sqrt() jump directly to the real address stored in the GOT, skipping the linker step.

The address 0x4006d0 you see is the address of this PLT stub, which lives in your executable's own memory space (low address range, typical for program code segments).

2. The dlopen-ed sqrt Address: The Real Function Entry

When you use dlopen to load libm.so.6 and dlsym to fetch sqrt, you're directly querying the dynamic library's symbol table for the actual address of the sqrt function code. This address (0x7fa7ce6f7250) lives in the memory region where libm.so.6 is loaded (high address range, typical for shared libraries).

There's no PLT/GOT indirection here—you're getting the raw entry point of the function in the shared library.


Is There an Indirect Mechanism?

Yes! The PLT/GOT system is the indirect mechanism at play for the directly linked sqrt. It's a core part of ELF dynamic linking that enables:

  • Lazy binding: Symbols are only resolved when they're first called, speeding up program startup.
  • Position-independent code (PIC): Shared libraries can be loaded at arbitrary memory addresses without needing to modify their code (the GOT holds the actual resolved addresses, so the library's code doesn't need hardcoded pointers).

Quick Verification

If you want to confirm the PLT stub is indeed a middleman, you can use objdump to look at your executable's PLT section:

objdump -R dl-test | grep sqrt

You'll see an entry pointing to the GOT, confirming that the sqrt symbol in your code is linked to the PLT/GOT system rather than the real function.

内容的提问来源于stack exchange,提问作者Aditi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:24:07