51

When I disassemble my small function, I happened to see this call

call   0xf60d2f47 <__i686.get_pc_thunk.bx>.

I have no clue why I need this call in my program. Any explanation would be helpful.

caf
  • 233,326
  • 40
  • 323
  • 462
Thangaraj
  • 3,038
  • 7
  • 40
  • 43

2 Answers2

63

This call is used in position-independent code on x86. It loads the position of the code into the %ebx register, which allows global objects (which have a fixed offset from the code) to be accessed as an offset from that register.

Position-independent code is code that can be loaded and executed, unmodified, at different addresses. It is important for code that will be linked into shared libraries, because these can be mapped at a different address in different processes.

Note that an equivalent call is not required on x86-64, because that architecture has IP-relative addressing modes (that is, it can directly address memory locations as an offset from the location of the current instruction).

caf
  • 233,326
  • 40
  • 323
  • 462
  • 2
    I agree, is there any good link to get more info on the same? – Thangaraj Jul 14 '11 at 10:01
  • @caf , why does x32 does not have IP-relative addressing mode? If I do `lea symbol(%rip), %rsi` it traslates to `lea symbol-.-7(%rip)`, so CPU does offset translation anyway (the first use is for convenience. So again - why does *not* have x32 IP-relative addressing mode? – autistic456 Jun 12 '20 at 18:48
  • @autistic456: That's 64-bit code. RIP-relative addressing was new in x86-64 (like this answer says), and isn't available in 32-bit code. (Look at `gcc -m32` output if you want to see how inconvenient 32-bit PIE / PIC code is.) As for why not, just historical reasons. 8086 didn't have it, and 386's changes to addressing-mode encodings (for 32-bit mode) didn't introduce EIP-relative addressing at the time. Position-independence wasn't as valuable then (when 386 was being designed) as it later became. – Peter Cordes Jun 12 '20 at 19:20
  • @PeterCordes why was *not* consider PIE executable, in time of designing x386? Does that mean, that in that time all programs were fixed at a absolute address, so when the memory was full, there was no left space for new process? And if more process accessed the same address entry, then there was a clash? As far as I know, the PIC is connected with all of those tables (GOF table, PLT table, and ohters), which are directly embedded in ELF format. Which mean that by the time of 386, there was no PIC executable and thus no ELF format? Or was it different? – autistic456 Jun 12 '20 at 21:14
  • @autistic456: Virtual memory gives each process its own private address space. Before that, 8086's solution to relocation was load-time fixups and/or segment registers. But on original 8086 RAM was so limited that you only ran one program at once anyway so there was no problem in the first place, that's why 8086 was designed that way. – Peter Cordes Jun 12 '20 at 21:18
  • @PeterCordes well, then how was the binary format adjusted to that? when there was no need for GOT table and other tables, since every address was calculated eihter from segment registers or in load time? It could not be ELF, since ELF is more "advance" for position independent code/executable? – autistic456 Jun 12 '20 at 21:22
  • @autistic456: That was up to the OS, not the CPU / ISA design. For example, I don't think PC-DOS or MS-DOS had shared libraries or dynamic linking at all. https://en.wikipedia.org/wiki/Dynamic-link_library says that was new with OS/2 and/or Windows. (Both of which predate GNU/Linux systems.) Of course 386 using virtual memory can do dynamic linking the modern way, with a GOT or equivalent. – Peter Cordes Jun 12 '20 at 21:28
  • 1
    @autistic456: As mentioned by PeterCordes, runtime relocation was achieved on 8086 by using segment-relative addressing. The 80286 design envisioned continuing in this style, by extending the segmentation capabilities of the processor to allow for an arbitrary 24 bit base and limit for each segment, and the 80386 extended this design still further. It later became apparent that a flat memory model was preferred, and this informed the later x86-64 design. – caf Jun 13 '20 at 02:06
  • @caf so the only way to achieve the position independency by the memory type was flat memory model? An as @PeterCordes mentioned, in particular for objects after relocation, their position was calculated via `ebx` register, and Peter mentioned, this was - by convention - used primarily by compilers. Is there any historical reason, that `ebx` was choosen as GOF offset for objects in PIE? Because of tha decision, the `ebx` was no longer "general" register, but need to be preserve (via `push` on stack), and I (assuming programming in 32) could no longer clober that register. So what was reason? – autistic456 Jun 13 '20 at 10:07
  • @autistic456: PIE is recent, but PIC (for shared libraries) has been around for a long time. https://web.archive.org/web/20140911162958/http://www.greyhat.ch/lab/downloads/pic.html says that PLT entries used to depend on EBX=GOT, so the choice of EBX was specified in the ABI doc. It's a good choice because it has few implicit uses (not needed for other instructions the way ECX is the shift counts, etc.) This is similar to why EBX is a good choice for being a call-preserved register: [x86 Assembly - Why is \[e\]bx preserved in calling conventions?](https://stackoverflow.com/q/22214208) – Peter Cordes Jun 13 '20 at 10:31
  • @autistic456: but note that in general functions can't depend on EBX=GOT having been set by their caller. e.g. [Eliminating redundant loads of GOT register?](https://stackoverflow.com/q/5075178). IDK if modern versions of the ABI still require EBX for calls through a PLT entry, or of the choice is now fully arbitrary even for functions that call across shared lib boundaries. – Peter Cordes Jun 13 '20 at 10:34
  • @autistic456: No, the idea is that if you are doing runtime relocation using segmentation, you don't need pc-relative addressing because all library addresses are relative to the segment base. The flat memory model with variable library load address necessitated pc-relative addressing, which brings us to the thunk in the question here. The x86-64 design then introduced the %rip-relative addressing mode to make this more convenient. – caf Jun 13 '20 at 11:23
11

Adding more to the information by example:

Suppose after you do disass on gdb inside function startup, then you will find something like this:

0x012c17a3  <startup+7>:     call   0x12b2ce7 <__i686.get_pc_thunk.bx>
0x012c17a8 <startup+12>:     add    $0x10d6518,%ebx

Then after you have called __i686.get_pc_thunk.bx, register ebx will be populated by value 0x012c17a8, which is the address of next instruction.

You can read the function as get_pc(program counter).

I found this article very nice for better understanding:

https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html

Ritesh
  • 1,809
  • 1
  • 14
  • 16