0

I have a binary depending on several shared libraries and I would like to produce a PIC binary except for one read-only table.

I want all code sections to be able to access this table without adhering to .GOTPCREL. I do not care in what kind of data section (.bss, .data, or etc) this table resides, but I want it to be read-only for sure and can be accessed directly by all shared libraries and the main binary. Therefore, I guess it should goes into the binary not in one of the shared libraries.

Currently I have it in .data.rel.ro section and it is read-only, however for accessing its data I need to get its address through .GOT like this: TABLENAME@GOTPCREL(%rip) and use the address afterwards. The reason I am asking that is that I would like to use bt instruction and for giving it the address of the table I need to have two instructions, and also one temporary register. If the table was not relocatable I was able to give the address of it in linking phase like: bt %REG, TABLENAME, requiring just one instruction without any extra register.

Is this something achievable at all?

Here is a more concrete example:

Main program code:

    .text
    ### SOME CODE 
    .section     .data.rel.ro,"aw",@progbits
    .globl  MYTABLE
MYTABLE:
    .zeros  128   # my table of 128 bytes all zeros for illustration

One of the shared-libraries:

    .text
    .globl  myfun
    .type   myfun, @function
myfun:
    xor     %rax, %rax
    movq    MYTABLE@GOTPCREL(%rip), %rcx   ### I like to have "bt %r11, MYABLE"
    bt      %r11, (%rcx)                   ### instead of these two lines
    jb      label
    movq    $1, %rax
label:
    ret
masec
  • 584
  • 5
  • 16
  • If you have a single non-relocatable section in a binary, the entire binary is no longer relocatable. That's the whole point of the meaning of relocatable. – fuz Dec 27 '18 at 18:30
  • That was actually my question. That if it is possible or not. I expected it could be the case that all code sections are relocatable except one particular data section. I still cannot understand why we cannot have such a partial relocatability? – masec Dec 27 '18 at 18:33
  • Wait a second, do you mean *position independent* (i.e. shared object) or *relocatable*? These two have different meanings. – fuz Dec 27 '18 at 18:47
  • I did not know they are different. I appreciate more explanation or a reference link to read more about that. I did a few quick searches and based on my understandings, I think I meant PIC. I want the executable and shared libraries to be position independent and I still want to access the table from shared libraries without using GOT. When I use the table without GOT I receive this ugly error that: relocation R_X86_64_PC32 against undefined symbol "blah" can not be used when making a shared object; recompile with -fPIC – masec Dec 27 '18 at 19:49
  • Can you show your assembly code? Usually, there is a way to avoid the GOT usage without sacrificing the PIC nature of your code. – fuz Dec 27 '18 at 20:05
  • A *relocatable file* is an object file, i.e. one where spots have not yet been filled in. The linker can go and fix up all these spots to turn the relocatable file into an executable file or shared object. The difference between an executable file and a shared object is that the former loads to a fixed address known at link time whereas the later can be loaded to any address. – fuz Dec 27 '18 at 20:25
  • Have you tried writing `bt %r11, MYTABLE(%rip)` instead? This uses a rip-relative addressing mode which should fix your problems. – fuz Dec 27 '18 at 20:26
  • Thanks for your explanation on relocatable file. I got all that except the last part. As far as I know the executable can also be loaded at any addresses as long as it is PIC. GCC I know that outputs a PIC executable by default. Regarding your suggestion, I tried that and I still get the same relocation error. – masec Dec 27 '18 at 20:35
  • 2
    Oh wait, I see now. I didn't quite see that the table is part of the main binary and you access it from shared libraries. No, there is no solution for that. You have to go through the GOT. – fuz Dec 27 '18 at 20:46
  • A position independent executable (PIE) is actually a form of shared library. Whether gcc generates a position independent executable by default depends on your system. – fuz Dec 27 '18 at 20:47
  • Yes. It is in the main binary. Actually it does not matter what my main is, I cannot make a shared library in the first place. Just wanted to make sure that it would be impossible. Still, I cannot get why is that? The address of the table can be determined at load time and the loader can fill that for the shared library that is being loaded at the same time. Any idea? I also appreciate if you can write your explanation as an answer. – masec Dec 27 '18 at 20:50
  • 1
    With ELF, the runtime linker does not patch any relocations at runtime. That does not happen. In fact, the entire ELF toolchain has been designed such that no relocations need to be patched at runtime. There might be a hack to make the runtime linker do that, but that's highly unexpected. The reason why the GOT and PLT exist is so that no relocations need to be patched. – fuz Dec 27 '18 at 20:52
  • Also, I don't really get why this is a problem. If performance is critical, load the address from the GOT once and keep it in a register. But the fact that you use `bt` with a memory operand already tells me that performance is likely not critical. – fuz Dec 27 '18 at 20:58
  • Well, performance always does matter. The `bt` with a register operand does not work as my table cannot be filled within a register. I would like to have a general solution for that and I prefer not to assume that one register is always available in the whole program. Thanks anyway. – masec Dec 27 '18 at 21:12
  • 2
    A `bt` with a memory operand is generally slower than manually computing the address and bit offset. That said, there is no solution to your problem. Your issue is pretty much a "WONTFIX by design" kind of thing. – fuz Dec 27 '18 at 21:59
  • How do you suggest manually computing the address and check if the bit is set or not? – masec Dec 27 '18 at 22:02
  • 1
    And the address with `7` to get the offset within a byte, shift right by 3 to get the byte number. This is curiously faster than a `bt` with a memory operand. – fuz Dec 27 '18 at 22:28
  • I mean, if you are not at a level of performance where instruction selection really matters, then you really don't have to worry about using an extra register for a pointer. – fuz Dec 27 '18 at 22:57
  • A conditional branch is also a bad idea here. After a `movzbl` load (zero-extending a byte into a register), you want something like `bt %r11d, %eax` / `setc %al`. And BTW, you could have the caller pass the address of the table. If the caller's in the main executable, it can use a RIP-relative LEA to put the address in a register, avoiding the GOT overhead. Or better, define this function in a header so it can inline. It's far too tiny to actually `call` / `ret`, and to force the boolean into a register instead of only existing in FLAGS if the caller does `if(myfun(5))`. – Peter Cordes Dec 30 '18 at 03:30

0 Answers0