What is _dl_lookup_symbol_x ? - C++ Profiling

2019-06-15 05:22发布

问题:

I'm using valgrind/callgrind to profile my server code for some optimization. The two most used calls that callgrind is reporting to me (using kcachegrind to view) are _dl_lookup_symbol_x and do_lookup_x. However I have no idea what either of these are and can't seem to find any documentation about them.

Could anyone please tell me where these two functions are used and what they do?

回答1:

_dl_lookup_symbol_x is an internal function inside the glibc C runtime library. If you browse the source for glibc, you'll find this comment above the _dl_lookup_symbol_x definition:

/* Search loaded objects' symbol tables for a definition of the symbol
   UNDEF_NAME, perhaps with a requested version for the symbol.

do_lookup_x is merely a helper function called within the _dl_lookup_symbol_x function.

I'm no expert on the internals of glibc, but from what I can gather, _dl_lookup_symbol_x looks for a symbol (such as a function) inside shared libraries loaded by your program.

I don't know why these functions are called so often in your profiling, but at least now you have some clue as to what they do. Your profiling should tell you what functions are responsible for calling _dl_lookup_symbol_x so often.

Note that it would be normal for _dl_lookup_symbol_x to be called many times when the program first starts, as the runtime figures out the addresses of shared library functions with a given name. If you're profiling a very short-lived program, then it's not surprising that you'd see that most of the time is spent in internal "housekeeping" functions rather than your own code.



回答2:

dl_lookup_symbol is called from _dl_fixup which is called from _dl_runtime_resolve_sse

In gdb if you break on the first function call, any call, then in lazy linking, which is default when using gcc, the function's address will not actually be resolved until after that call pretty much returns to main. One of the functions that the process of uses to resolve the address of the function is dl_lookup_symbol, so you should expect to see it once for each function that is called by every program that uses lazy linking, pretty much all of them.

Source code:

#include <stdio.h>
#include <string.h>

int main (void)
{
    size_t a = strlen("Hello"); 
    printf("%s is %d letters\n", "Hello", a);

    if (a != sizeof("Hello") - 1)
    {
        perror("What?");
    }

    size_t b = strlen("Bye");
    printf("%s is %d letters\n", "Bye", b);

    if (b != sizeof("Bye") - 1)
    {
        perror("What?");
    }

    return 0;
}

Going to gdb:

(gdb) disas
Dump of assembler code for function main:
   0x0000555555554700 <+0>: push   rbp
   0x0000555555554701 <+1>: mov    rbp,rsp
   0x0000555555554704 <+4>: sub    rsp,0x10
   0x0000555555554708 <+8>: mov    QWORD PTR [rbp-0x8],0x5
   0x0000555555554710 <+16>:    mov    rax,QWORD PTR [rbp-0x8]
   0x0000555555554714 <+20>:    mov    rdx,rax
   0x0000555555554717 <+23>:    lea    rsi,[rip+0xf6]        # 0x555555554814
   0x000055555555471e <+30>:    lea    rdi,[rip+0xf5]        # 0x55555555481a
   0x0000555555554725 <+37>:    mov    eax,0x0
=> 0x000055555555472a <+42>:    call   0x5555555545a0 <printf@plt>
   0x000055555555472f <+47>:    cmp    QWORD PTR [rbp-0x8],0x5
   0x0000555555554734 <+52>:    je     0x555555554742 <main+66>
   0x0000555555554736 <+54>:    lea    rdi,[rip+0xef]        # 0x55555555482c
   0x000055555555473d <+61>:    call   0x5555555545b0 <perror@plt>
   0x0000555555554742 <+66>:    mov    QWORD PTR [rbp-0x10],0x3
   0x000055555555474a <+74>:    mov    rax,QWORD PTR [rbp-0x10]
   0x000055555555474e <+78>:    mov    rdx,rax
   0x0000555555554751 <+81>:    lea    rsi,[rip+0xda]        # 0x555555554832
   0x0000555555554758 <+88>:    lea    rdi,[rip+0xbb]        # 0x55555555481a
   0x000055555555475f <+95>:    mov    eax,0x0
   0x0000555555554764 <+100>:   call   0x5555555545a0 <printf@plt>
   0x0000555555554769 <+105>:   cmp    QWORD PTR [rbp-0x10],0x3
   0x000055555555476e <+110>:   je     0x55555555477c <main+124>
   0x0000555555554770 <+112>:   lea    rdi,[rip+0xb5]        # 0x55555555482c
   0x0000555555554777 <+119>:   call   0x5555555545b0 <perror@plt>
   0x000055555555477c <+124>:   mov    eax,0x0
   0x0000555555554781 <+129>:   leave  
   0x0000555555554782 <+130>:   ret    

The following, after stepping into printf, is where lazy linking's magic results show up, after it's done. Here I'm just showing that as I step through this, the program walks through, but once printf is resolved, if I stepi at

0x00005555555545a0 <+0>:    jmp    QWORD PTR [rip+0x200a72]

the program will go straight to printf. So, next time the function is called jmp QWORD PTR [rip+0x200a72] will take us straight to printf. All code after this point and until the function returns to main is just the resolving of printf's address and only happens the first time that the function is called.

(gdb) disas
Dump of assembler code for function printf@plt:
=> 0x00005555555545a0 <+0>: jmp    QWORD PTR [rip+0x200a72]        # 0x555555755018
   0x00005555555545a6 <+6>: push   0x0
   0x00005555555545ab <+11>:    jmp    0x555555554590

(gdb) disas
Dump of assembler code for function printf@plt:
=> 0x00005555555545a0 <+0>: jmp    QWORD PTR [rip+0x200a72]        # 0x555555755018
   0x00005555555545a6 <+6>: push   0x0
   0x00005555555545ab <+11>:    jmp    0x555555554590
End of assembler dump.
(gdb) stepi
0x00005555555545a6 in printf@plt ()
(gdb) disas
Dump of assembler code for function printf@plt:
   0x00005555555545a0 <+0>: jmp    QWORD PTR [rip+0x200a72]        # 0x555555755018
=> 0x00005555555545a6 <+6>: push   0x0
   0x00005555555545ab <+11>:    jmp    0x555555554590
End of assembler dump.
(gdb) stepi
0x00005555555545ab in printf@plt ()
(gdb) disas
Dump of assembler code for function printf@plt:
   0x00005555555545a0 <+0>: jmp    QWORD PTR [rip+0x200a72]        # 0x555555755018
   0x00005555555545a6 <+6>: push   0x0
=> 0x00005555555545ab <+11>:    jmp    0x555555554590

Stepping in. Not sure why gdb doesn't pick these up, they are just instructions in the .plt table. And yes the addresses aren't the same, my system adds 0x555555554000 to everything before we even hit _start

(gdb) stepi
0x0000555555554590 in ?? ()
(gdb) disas
No function contains program counter for selected frame.
(gdb) stepi
0x0000555555554596 in ?? ()
(gdb) disas
No function contains program counter for selected frame.

, proof:

$ gdb -q ./simpletest
Reading symbols from ./simpletest...done.
(gdb) break _start
Breakpoint 1 at 0x5d0
(gdb) run
Starting program: /flerb/flerb/flerb/simpletest 

Breakpoint 1, 0x00005555555545d0 in _start ()
(gdb) 

From objdump:

Disassembly of section .plt:

0000000000000590 <.plt>:
 590:   ff 35 72 0a 20 00       push   QWORD PTR [rip+0x200a72]        # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
 596:   ff 25 74 0a 20 00       jmp    QWORD PTR [rip+0x200a74]        # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
 59c:   0f 1f 40 00             nop    DWORD PTR [rax+0x0]

gdb

(gdb) stepi
_dl_runtime_resolve_sse () at ../sysdeps/x86_64/dl-trampoline.h:164
164 ../sysdeps/x86_64/dl-trampoline.h: No such file or directory.

Hey, _dl_runtime_resolve_sse, we're getting closer.The following might look ugly. It's not. It's just saving registers so that the call 0x00007ffff7def11a <+154>: call 0x7ffff7de7970 <_dl_fixup> halfway through can use whatever registers it needs to, and that's truly where the magic happens. At the end 0x00007ffff7def1b0 <+304>: bnd jmp r11 will take us to printf just this one time that the program goes through this code for this particular function, because it's the only time that the program goes through this code for this particular function.

(gdb) disas
Dump of assembler code for function _dl_runtime_resolve_sse:
=> 0x00007ffff7def080 <+0>: push   rbx
   0x00007ffff7def081 <+1>: mov    rbx,rsp
   0x00007ffff7def084 <+4>: and    rsp,0xfffffffffffffff0
   0x00007ffff7def088 <+8>: sub    rsp,0x100
   0x00007ffff7def08f <+15>:    mov    QWORD PTR [rsp+0xc0],rax
   0x00007ffff7def097 <+23>:    mov    QWORD PTR [rsp+0xc8],rcx
   0x00007ffff7def09f <+31>:    mov    QWORD PTR [rsp+0xd0],rdx
   0x00007ffff7def0a7 <+39>:    mov    QWORD PTR [rsp+0xd8],rsi
   0x00007ffff7def0af <+47>:    mov    QWORD PTR [rsp+0xe0],rdi
   0x00007ffff7def0b7 <+55>:    mov    QWORD PTR [rsp+0xe8],r8
   0x00007ffff7def0bf <+63>:    mov    QWORD PTR [rsp+0xf0],r9
   0x00007ffff7def0c7 <+71>:    movaps XMMWORD PTR [rsp],xmm0
   0x00007ffff7def0cb <+75>:    movaps XMMWORD PTR [rsp+0x10],xmm1
   0x00007ffff7def0d0 <+80>:    movaps XMMWORD PTR [rsp+0x20],xmm2
   0x00007ffff7def0d5 <+85>:    movaps XMMWORD PTR [rsp+0x30],xmm3
   0x00007ffff7def0da <+90>:    movaps XMMWORD PTR [rsp+0x40],xmm4
   0x00007ffff7def0df <+95>:    movaps XMMWORD PTR [rsp+0x50],xmm5
   0x00007ffff7def0e4 <+100>:   movaps XMMWORD PTR [rsp+0x60],xmm6
   0x00007ffff7def0e9 <+105>:   movaps XMMWORD PTR [rsp+0x70],xmm7
   0x00007ffff7def0ee <+110>:   bndmov [rsp+0x80],bnd0
   0x00007ffff7def0f7 <+119>:   bndmov [rsp+0x90],bnd1
   0x00007ffff7def100 <+128>:   bndmov [rsp+0xa0],bnd2
   0x00007ffff7def109 <+137>:   bndmov [rsp+0xb0],bnd3
   0x00007ffff7def112 <+146>:   mov    rsi,QWORD PTR [rbx+0x10]
   0x00007ffff7def116 <+150>:   mov    rdi,QWORD PTR [rbx+0x8]

   0x00007ffff7def11a <+154>:   call   0x7ffff7de7970 <_dl_fixup>
   0x00007ffff7def11f <+159>:   mov    r11,rax

   0x00007ffff7def122 <+162>:   bndmov bnd3,[rsp+0xb0]
   0x00007ffff7def12b <+171>:   bndmov bnd2,[rsp+0xa0]
   0x00007ffff7def134 <+180>:   bndmov bnd1,[rsp+0x90]
   0x00007ffff7def13d <+189>:   bndmov bnd0,[rsp+0x80]
   0x00007ffff7def146 <+198>:   mov    r9,QWORD PTR [rsp+0xf0]
   0x00007ffff7def14e <+206>:   mov    r8,QWORD PTR [rsp+0xe8]
   0x00007ffff7def156 <+214>:   mov    rdi,QWORD PTR [rsp+0xe0]
   0x00007ffff7def15e <+222>:   mov    rsi,QWORD PTR [rsp+0xd8]
   0x00007ffff7def166 <+230>:   mov    rdx,QWORD PTR [rsp+0xd0]
   0x00007ffff7def16e <+238>:   mov    rcx,QWORD PTR [rsp+0xc8]
   0x00007ffff7def176 <+246>:   mov    rax,QWORD PTR [rsp+0xc0]
   0x00007ffff7def17e <+254>:   movaps xmm0,XMMWORD PTR [rsp]
   0x00007ffff7def182 <+258>:   movaps xmm1,XMMWORD PTR [rsp+0x10]
   0x00007ffff7def187 <+263>:   movaps xmm2,XMMWORD PTR [rsp+0x20]
   0x00007ffff7def18c <+268>:   movaps xmm3,XMMWORD PTR [rsp+0x30]
   0x00007ffff7def191 <+273>:   movaps xmm4,XMMWORD PTR [rsp+0x40]
   0x00007ffff7def196 <+278>:   movaps xmm5,XMMWORD PTR [rsp+0x50]
   0x00007ffff7def19b <+283>:   movaps xmm6,XMMWORD PTR [rsp+0x60]
   0x00007ffff7def1a0 <+288>:   movaps xmm7,XMMWORD PTR [rsp+0x70]
   0x00007ffff7def1a5 <+293>:   mov    rsp,rbx
   0x00007ffff7def1a8 <+296>:   mov    rbx,QWORD PTR [rsp]
   0x00007ffff7def1ac <+300>:   add    rsp,0x18

   0x00007ffff7def1b0 <+304>:   bnd jmp r11

End of assembler dump.

that bnd jmp r11 at the end will take us to printf.

Lastly we step into _dl_fixup, where I singled out the call to _dl_lookup_symbol

(gdb) break *0x7ffff7def11a 
Note: breakpoint 5 also set at pc 0x7ffff7def11a.
Breakpoint 8 at 0x7ffff7def11a: file ../sysdeps/x86_64/dl-trampoline.h, line 212.
(gdb) cont
Continuing.

Breakpoint 5, _dl_runtime_resolve_sse () at ../sysdeps/x86_64/dl-trampoline.h:212
212 in ../sysdeps/x86_64/dl-trampoline.h
(gdb) stepi
_dl_fixup (l=0x7ffff7ffe168, reloc_arg=0) at ../elf/dl-runtime.c:66
66  ../elf/dl-runtime.c: No such file or directory.
(gdb) disas
Dump of assembler code for function _dl_fixup:
=> 0x00007ffff7de7970 <+0>: push   rbx
   0x00007ffff7de7971 <+1>: mov    r10,rdi
   0x00007ffff7de7974 <+4>: mov    esi,esi
   0x00007ffff7de7976 <+6>: lea    rdx,[rsi+rsi*2]
   0x00007ffff7de797a <+10>:    sub    rsp,0x10
   0x00007ffff7de797e <+14>:    mov    rax,QWORD PTR [rdi+0x68]
   0x00007ffff7de7982 <+18>:    mov    rdi,QWORD PTR [rax+0x8]
   0x00007ffff7de7986 <+22>:    mov    rax,QWORD PTR [r10+0xf8]
   0x00007ffff7de798d <+29>:    mov    rax,QWORD PTR [rax+0x8]
   0x00007ffff7de7991 <+33>:    lea    r8,[rax+rdx*8]
   0x00007ffff7de7995 <+37>:    mov    rax,QWORD PTR [r10+0x70]
   0x00007ffff7de7999 <+41>:    mov    rcx,QWORD PTR [r8+0x8]
   0x00007ffff7de799d <+45>:    mov    rax,QWORD PTR [rax+0x8]
   0x00007ffff7de79a1 <+49>:    mov    rdx,rcx
   0x00007ffff7de79a4 <+52>:    shr    rdx,0x20
   0x00007ffff7de79a8 <+56>:    lea    rsi,[rdx+rdx*2]
   0x00007ffff7de79ac <+60>:    lea    rsi,[rax+rsi*8]
   0x00007ffff7de79b0 <+64>:    mov    rax,QWORD PTR [r10]
   0x00007ffff7de79b3 <+67>:    mov    QWORD PTR [rsp+0x8],rsi
   0x00007ffff7de79b8 <+72>:    mov    rbx,rax
   0x00007ffff7de79bb <+75>:    add    rbx,QWORD PTR [r8]
   0x00007ffff7de79be <+78>:    cmp    ecx,0x7
   0x00007ffff7de79c1 <+81>:    jne    0x7ffff7de7b17 <_dl_fixup+423>
   0x00007ffff7de79c7 <+87>:    test   BYTE PTR [rsi+0x5],0x3
   0x00007ffff7de79cb <+91>:    jne    0x7ffff7de7a67 <_dl_fixup+247>
   0x00007ffff7de79d1 <+97>:    mov    rax,QWORD PTR [r10+0x1c8]
   0x00007ffff7de79d8 <+104>:   test   rax,rax
   0x00007ffff7de79db <+107>:   je     0x7ffff7de7a90 <_dl_fixup+288>
   0x00007ffff7de79e1 <+113>:   mov    rax,QWORD PTR [rax+0x8]
   0x00007ffff7de79e5 <+117>:   movzx  eax,WORD PTR [rax+rdx*2]
   0x00007ffff7de79e9 <+121>:   and    eax,0x7fff
   0x00007ffff7de79ee <+126>:   lea    rdx,[rax+rax*2]
   0x00007ffff7de79f2 <+130>:   mov    rax,QWORD PTR [r10+0x2e0]
   0x00007ffff7de79f9 <+137>:   lea    r8,[rax+rdx*8]
   0x00007ffff7de79fd <+141>:   mov    eax,0x0
   0x00007ffff7de7a02 <+146>:   mov    r9d,DWORD PTR [r8+0x8]
   0x00007ffff7de7a06 <+150>:   test   r9d,r9d
   0x00007ffff7de7a09 <+153>:   cmove  r8,rax
   0x00007ffff7de7a0d <+157>:   mov    edx,DWORD PTR fs:0x18
   0x00007ffff7de7a15 <+165>:   test   edx,edx
   0x00007ffff7de7a17 <+167>:   mov    eax,0x1
   0x00007ffff7de7a1c <+172>:   jne    0x7ffff7de7ab0 <_dl_fixup+320>
   0x00007ffff7de7a22 <+178>:   mov    esi,DWORD PTR [rsi]
   0x00007ffff7de7a24 <+180>:   mov    rcx,QWORD PTR [r10+0x380]
   0x00007ffff7de7a2b <+187>:   lea    rdx,[rsp+0x8]
   0x00007ffff7de7a30 <+192>:   push   0x0
   0x00007ffff7de7a32 <+194>:   push   rax
   0x00007ffff7de7a33 <+195>:   mov    r9d,0x1
   0x00007ffff7de7a39 <+201>:   add    rdi,rsi
   0x00007ffff7de7a3c <+204>:   mov    rsi,r10

   0x00007ffff7de7a3f <+207>:   call   0x7ffff7de2d10 <_dl_lookup_symbol_x>

   0x00007ffff7de7a44 <+212>:   mov    r8,rax
   0x00007ffff7de7a47 <+215>:   mov    eax,DWORD PTR fs:0x18
   0x00007ffff7de7a4f <+223>:   test   eax,eax
   0x00007ffff7de7a51 <+225>:   pop    rcx
   0x00007ffff7de7a52 <+226>:   pop    rsi
   0x00007ffff7de7a53 <+227>:   jne    0x7ffff7de7ad0 <_dl_fixup+352>
   0x00007ffff7de7a55 <+229>:   mov    rsi,QWORD PTR [rsp+0x8]
   0x00007ffff7de7a5a <+234>:   test   rsi,rsi
   0x00007ffff7de7a5d <+237>:   je     0x7ffff7de7aa0 <_dl_fixup+304>
   0x00007ffff7de7a5f <+239>:   test   r8,r8
   0x00007ffff7de7a62 <+242>:   je     0x7ffff7de7aa8 <_dl_fixup+312>
   0x00007ffff7de7a64 <+244>:   mov    rax,QWORD PTR [r8]
   0x00007ffff7de7a67 <+247>:   movzx  edx,BYTE PTR [rsi+0x4]
   0x00007ffff7de7a6b <+251>:   add    rax,QWORD PTR [rsi+0x8]
   0x00007ffff7de7a6f <+255>:   and    edx,0xf
   0x00007ffff7de7a72 <+258>:   cmp    dl,0xa
   0x00007ffff7de7a75 <+261>:   je     0x7ffff7de7b10 <_dl_fixup+416>
   0x00007ffff7de7a7b <+267>:   mov    edx,DWORD PTR [rip+0x215267]        # 0x7ffff7ffcce8 <_rtld_global_ro+72>
   0x00007ffff7de7a81 <+273>:   test   edx,edx
   0x00007ffff7de7a83 <+275>:   jne    0x7ffff7de7a88 <_dl_fixup+280>
   0x00007ffff7de7a85 <+277>:   mov    QWORD PTR [rbx],rax
   0x00007ffff7de7a88 <+280>:   add    rsp,0x10
   0x00007ffff7de7a8c <+284>:   pop    rbx
   0x00007ffff7de7a8d <+285>:   ret    
   0x00007ffff7de7a8e <+286>:   xchg   ax,ax
   0x00007ffff7de7a90 <+288>:   xor    r8d,r8d
   0x00007ffff7de7a93 <+291>:   jmp    0x7ffff7de7a0d <_dl_fixup+157>
   0x00007ffff7de7a98 <+296>:   nop    DWORD PTR [rax+rax*1+0x0]
   0x00007ffff7de7aa0 <+304>:   xor    eax,eax
   0x00007ffff7de7aa2 <+306>:   jmp    0x7ffff7de7a7b <_dl_fixup+267>
   0x00007ffff7de7aa4 <+308>:   nop    DWORD PTR [rax+0x0]
   0x00007ffff7de7aa8 <+312>:   xor    eax,eax
   0x00007ffff7de7aaa <+314>:   jmp    0x7ffff7de7a67 <_dl_fixup+247>
   0x00007ffff7de7aac <+316>:   nop    DWORD PTR [rax+0x0]
   0x00007ffff7de7ab0 <+320>:   mov    DWORD PTR fs:0x1c,0x1
   0x00007ffff7de7abc <+332>:   mov    eax,0x5
   0x00007ffff7de7ac1 <+337>:   jmp    0x7ffff7de7a22 <_dl_fixup+178>
   0x00007ffff7de7ac6 <+342>:   nop    WORD PTR cs:[rax+rax*1+0x0]
   0x00007ffff7de7ad0 <+352>:   xor    eax,eax
   0x00007ffff7de7ad2 <+354>:   xchg   DWORD PTR fs:0x1c,eax
   0x00007ffff7de7ada <+362>:   cmp    eax,0x2
   0x00007ffff7de7add <+365>:   jne    0x7ffff7de7a55 <_dl_fixup+229>
   0x00007ffff7de7ae3 <+371>:   mov    rdi,QWORD PTR fs:0x10
   0x00007ffff7de7aec <+380>:   xor    r10d,r10d
   0x00007ffff7de7aef <+383>:   add    rdi,0x1c
   0x00007ffff7de7af3 <+387>:   mov    edx,0x1
   0x00007ffff7de7af8 <+392>:   mov    esi,0x81
   0x00007ffff7de7afd <+397>:   mov    eax,0xca
   0x00007ffff7de7b02 <+402>:   syscall 
   0x00007ffff7de7b04 <+404>:   jmp    0x7ffff7de7a55 <_dl_fixup+229>
   0x00007ffff7de7b09 <+409>:   nop    DWORD PTR [rax+0x0]
   0x00007ffff7de7b10 <+416>:   call   rax
   0x00007ffff7de7b12 <+418>:   jmp    0x7ffff7de7a7b <_dl_fixup+267>
   0x00007ffff7de7b17 <+423>:   lea    rcx,[rip+0x10862]        # 0x7ffff7df8380 <__PRETTY_FUNCTION__.10645>
   0x00007ffff7de7b1e <+430>:   lea    rsi,[rip+0xea8f]        # 0x7ffff7df65b4
   0x00007ffff7de7b25 <+437>:   lea    rdi,[rip+0x1081c]        # 0x7ffff7df8348
   0x00007ffff7de7b2c <+444>:   mov    edx,0x4f
   0x00007ffff7de7b31 <+449>:   call   0x7ffff7df1210 <__GI___assert_fail>
End of assembler dump.

This is as far as this journey goes. the dl_lookup_symbol function goes to a land of parsing elf files and working with symbols, that's where the pfm happens.