In some disassembly I see jal ra sym.imp.strlen
which jumps to a construct which looks like this in radare2:
┌ 128: size_t sym.imp.strlen (const char *s);
│ 0x00035b40 172eb600 auipc t3, 0xb62
│ 0x00035b44 033e0e75 ld t3, 1872(t3)
│ 0x00035b48 67030e00 jalr t1, t3
│ 0x00035b4c 13000000 nop
│ ;-- dlclose:
│ 0x00035b50 172eb600 auipc t3, 0xb62
│ 0x00035b54 033e8e74 ld t3, 1864(t3)
│ 0x00035b58 67030e00 jalr t1, t3
│ 0x00035b5c 13000000 nop
│ ;-- erff:
│ 0x00035b60 172eb600 auipc t3, 0xb62
│ 0x00035b64 033e0e74 ld t3, 1856(t3)
│ 0x00035b68 67030e00 jalr t1, t3
│ 0x00035b6c 13000000 nop
│ ;-- truncf:
│ 0x00035b70 172eb600 auipc t3, 0xb62
│ 0x00035b74 033e8e73 ld t3, 1848(t3)
│ 0x00035b78 67030e00 jalr t1, t3
│ 0x00035b7c 13000000 nop
│ ;-- strncpy:
│ 0x00035b80 172eb600 auipc t3, 0xb62
│ 0x00035b84 033e0e73 ld t3, 1840(t3)
│ 0x00035b88 67030e00 jalr t1, t3
│ 0x00035b8c 13000000 nop
│ ;-- write:
│ 0x00035b90 172eb600 auipc t3, 0xb62
│ 0x00035b94 033e8e72 ld t3, 1832(t3)
│ 0x00035b98 67030e00 jalr t1, t3
│ 0x00035b9c 13000000 nop
│ ;-- fesetround:
│ 0x00035ba0 172eb600 auipc t3, 0xb62
│ 0x00035ba4 033e0e72 ld t3, 1824(t3)
│ 0x00035ba8 67030e00 jalr t1, t3
│ 0x00035bac 13000000 nop
│ ;-- sincos:
│ 0x00035bb0 172eb600 auipc t3, 0xb62
│ 0x00035bb4 033e8e71 ld t3, 1816(t3)
│ 0x00035bb8 67030e00 jalr t1, t3
└ 0x00035bbc 13000000 nop
Is this a jump table?
It seems strange to me to jump to the strlen function so indirectly. It jumps first to this table, then uses auipc
(so the address of the table entry plus 0xb62 << 12
) to calculate an address to load. Loads the address + some offset (I assume this is the entry point of strlen
) and then jumps there. Why not just have a symbol for jumping directly to strlen
's entry point?
One possible justification I can think of is code compression - it may be more compact to do this rather than loading/jumping to a symbol at every place strlen
is called. I also have a feeling it could be something to do with linking?