Skip to content

Instantly share code, notes, and snippets.

@andrewrk
Last active October 6, 2021 19:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewrk/7187b28dfdd581f55ede72bf2b317c60 to your computer and use it in GitHub Desktop.
Save andrewrk/7187b28dfdd581f55ede72bf2b317c60 to your computer and use it in GitHub Desktop.
trying to figure out what's going wrong with this linking

I have an object file created by LLVM:

$ readelf -s test.o

Symbol table '.symtab' contains 8 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test
     2: 0000000000000010    22 FUNC    LOCAL  DEFAULT    2 start.callMain2
     3: 0000000000000030   213 FUNC    LOCAL  DEFAULT    2 test.main
     4: 0000000000000110    18 FUNC    LOCAL  DEFAULT    2 start.exit2
     5: 0000000000000000     5 FUNC    GLOBAL DEFAULT    2 _start
     6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __extenddftf2
     7: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND __eqtf2

You can see that it makes an external function call to __extenddftf2 as well as __eqtf2 which both need to be resolved by the linker.

Here I have a compiler-rt.o file:

$ readelf -s compiler-rt.o

Symbol table '.symtab' contains 40 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS compiler_rt
     2: 0000000000000860    21 FUNC    LOCAL  DEFAULT    1 math.inf.inf__anon_38
     3: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    3 .LCPI5_0
     4: 0000000000000a40    21 FUNC    LOCAL  DEFAULT    1 math.inf.inf__anon_40
     5: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    4 .LCPI7_0
     6: 0000000000000cb0    18 FUNC    LOCAL  DEFAULT    1 math.inf.inf__anon_42
     7: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    5 .LCPI9_0
     8: 00000000000013f0    34 FUNC    LOCAL  DEFAULT    1 debug.assert
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 
    11: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 
    12: 0000000000000000     0 SECTION LOCAL  DEFAULT    4 
    13: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 
    14: 0000000000000000   500 FUNC    GLOBAL DEFAULT    1 __extenddftf2
    15: 0000000000000200   427 FUNC    GLOBAL DEFAULT    1 __extendsftf2
    16: 00000000000003b0   329 FUNC    GLOBAL DEFAULT    1 __extendhfsf2
    17: 0000000000000500   450 FUNC    GLOBAL DEFAULT    1 __extendhftf2
    18: 00000000000006d0   393 FUNC    GLOBAL DEFAULT    1 __lesf2
    19: 0000000000000880   440 FUNC    GLOBAL DEFAULT    1 __ledf2
    20: 0000000000000a60   582 FUNC    GLOBAL DEFAULT    1 __letf2
    21: 0000000000000cd0   390 FUNC    GLOBAL DEFAULT    1 __gesf2
    22: 0000000000000e60   437 FUNC    GLOBAL DEFAULT    1 __gedf2
    23: 0000000000001020   579 FUNC    GLOBAL DEFAULT    1 __getf2
    24: 0000000000001270    46 FUNC    GLOBAL DEFAULT    1 __eqsf2
    25: 00000000000012a0    46 FUNC    GLOBAL DEFAULT    1 __eqdf2
    26: 00000000000012d0    46 FUNC    GLOBAL DEFAULT    1 __ltsf2
    27: 0000000000001300    46 FUNC    GLOBAL DEFAULT    1 __ltdf2
    28: 0000000000001330    46 FUNC    GLOBAL DEFAULT    1 __nesf2
    29: 0000000000001360    46 FUNC    GLOBAL DEFAULT    1 __nedf2
    30: 0000000000001390    46 FUNC    GLOBAL DEFAULT    1 __gtsf2
    31: 00000000000013c0    46 FUNC    GLOBAL DEFAULT    1 __gtdf2
    32: 00000000000003b0   329 FUNC    GLOBAL DEFAULT    1 __gnu_h2f_ieee
    33: 00000000000006d0   393 FUNC    GLOBAL DEFAULT    1 __cmpsf2
    34: 0000000000000880   440 FUNC    GLOBAL DEFAULT    1 __cmpdf2
    35: 0000000000000a60   582 FUNC    GLOBAL DEFAULT    1 __cmptf2
    36: 0000000000000a60   582 FUNC    GLOBAL DEFAULT    1 __eqtf2
    37: 0000000000000a60   582 FUNC    GLOBAL DEFAULT    1 __lttf2
    38: 0000000000000a60   582 FUNC    GLOBAL DEFAULT    1 __netf2
    39: 0000000000001020   579 FUNC    GLOBAL DEFAULT    1 __gttf2

You can see it provides both functions. However, after linking:

ld.lld -error-limit=0 -z stack-size=16777216 --gc-sections -m elf_x86_64 -static -o test test.o /home/andy/.cache/zig/o/8fba243ef4ba3750d1cf9c276017e573/libc.a /home/andy/.cache/zig/o/a694ba35d496c525af985782dbb765f3/libcompiler_rt.a

The __cmptf2 function is

$ readelf -s test

Symbol table '.symtab' contains 13 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS test
     2: 0000000000201130    22 FUNC    LOCAL  DEFAULT    1 start.callMain2
     3: 0000000000201150   213 FUNC    LOCAL  DEFAULT    1 test.main
     4: 0000000000201230    18 FUNC    LOCAL  DEFAULT    1 start.exit2
     5: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS compiler_rt
     6: 0000000000201120     5 FUNC    GLOBAL DEFAULT    1 _start
     7: 0000000000201250   221 FUNC    GLOBAL DEFAULT    1 __extenddftf2
     8: 0000000000201330     0 FUNC    GLOBAL DEFAULT    1 __eqtf2
     9: 0000000000201330     0 FUNC    GLOBAL DEFAULT    1 __letf2
    10: 0000000000201330     0 FUNC    GLOBAL DEFAULT    1 __cmptf2
    11: 0000000000201330     0 FUNC    GLOBAL DEFAULT    1 __lttf2
    12: 0000000000201330     0 FUNC    GLOBAL DEFAULT    1 __netf2

The binary crashes because the function call to __eqtf2 has been deleted (the Size is 0). However the call to __extenddftf2 has been preserved, as expected.

  2011e2:	e8 49 01 00 00       	call   201330 <__cmptf2>

There is nothing at address 201330. What gives?

@andrewrk
Copy link
Author

andrewrk commented Oct 5, 2021

The problem went away when I deleted all the aliases of __eqtf2

@andrewrk
Copy link
Author

andrewrk commented Oct 6, 2021

The ultimate solution to this problem was a mismatch between declared function calling convention and the calling convention used at the callsite. This problem is exacerbated by (1) the fact that LLVM C API for creating a function call does not allow specifying the calling convention; you have to use the C++ API to do so, (2) LLVM IR did not used to have this requirement, and (3) LLVM module verification checks do not check this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment