Question about relocation type and dynamic library


I hope someone can educate me on this. :slight_smile:
Recently I encountered code where object file had relocation of type R_X86_64_32S, and definition of it was in some python .so file. The object file was build without -fpic. This was with llvm-12:

00000000004006e0 <_ZN22shared_ptr_from_python11convertibleEP7_object>:
  4006e0: b9 28 10 60 00                movl  $0x601028, %ecx         # imm = 0x601028
    00000000004006e1:  R_X86_64_32  _Py_NoneStruct
  4006e5: 31 c0                         xorl  %eax, %eax
  4006e7: 48 81 ff 28 10 60 00          cmpq  $0x601028, %rdi         # imm = 0x601028
    00000000004006ea:  R_X86_64_32S _Py_NoneStruct
  4006ee: 48 0f 44 c1                   cmoveq  %rcx, %rax
  4006f2: c3                            retq
  4006f3: 66 2e 0f 1f 84 00 00 00 00 00 nopw  %cs:(%rax,%rax)
  4006fd: 0f 1f 00                      nopl  (%rax)

With llvm-15/trunk it is using what I would expect:

0000000000001160 <_ZN22shared_ptr_from_python11convertibleEP7_object>:
    1160: 48 8b 0d 71 2e 00 00          movq  0x2e71(%rip), %rcx      # 0x3fd8 <main.cpp+0x3fd8>
    0000000000001163:  R_X86_64_REX_GOTPCRELX _Py_NoneStruct-0x4
    1167: 31 c0                         xorl  %eax, %eax
    1169: 48 39 cf                      cmpq  %rcx, %rdi
    116c: 48 0f 44 c1                   cmoveq  %rcx, %rax
    1170: c3                            retq
    1171: 66 2e 0f 1f 84 00 00 00 00 00 nopw  %cs:(%rax,%rax)
    117b: 0f 1f 44 00 00                nopl  (%rax,%rax)

I think I am missing something, but shouldn’t it always go through GOT since it’s not known where dynamic linker will load the dynamic library?

Small(ish) Repro build with -O3

extern "C" {
typedef struct _object {
  int p;
} PyObject;
extern PyObject _Py_NoneStruct;
struct shared_ptr_from_python {
void *(*foo)(PyObject *p);
shared_ptr_from_python() {
    foo = &convertible;
static void* convertible(PyObject *p) {
  if (p == (&_Py_NoneStruct))
    return p;
  return 0;


#include "helper.hpp"
extern int helper2(shared_ptr_from_python &ptr);
extern PyObject *_Py_NoneStruct2;
int main(){
shared_ptr_from_python ptr;
return helper2(ptr);

If it’s built without PIC then it’s not shared library code, by definition. The difference you see is because LLVM 15 enabled PIE by default on Linux, whereas prior to that it would default to entirely position-dependent.

It is being linked against a so build with -fPIC -shared. I guess rephrased question is why does this work? Should the .o also have been build with PIC?

Yes, that’s always been the case and continues to be

Had some time to circle back to this. Reason it works is LLD generates R_X86_64_COPY relocation. Mystery solved.

If you’re linking a position-dependent executable (or a position-independent executable that uses PC-relative addressing without indirection via the GOT), yes, copy relocations are the gross way in which things are made to work.