diff --git a/lld/ELF/Arch/Mips.cpp b/lld/ELF/Arch/Mips.cpp --- a/lld/ELF/Arch/Mips.cpp +++ b/lld/ELF/Arch/Mips.cpp @@ -83,6 +83,17 @@ switch (type) { case R_MIPS_JALR: + // Older versions of clang would erroneously emit this relocation not only + // against functions (loaded from the GOT) but also against data symbols + // (e.g. a table of function pointers). When we encounter this, ignore the + // relocation and emit a warning instead. + if (!s.isFunc()) { + warn(getErrorLocation(loc) + + "found R_MIPS_JALR relocation against non-function symbol " + + toString(s) + ". This is invalid and most likely a compiler bug."); + return R_NONE; + } + // If the target symbol is not preemptible and is not microMIPS, // it might be possible to replace jalr/jr instruction by bal/b. // It depends on the target symbol's offset. diff --git a/lld/test/ELF/mips-r-jalr-non-functions.s b/lld/test/ELF/mips-r-jalr-non-functions.s new file mode 100644 --- /dev/null +++ b/lld/test/ELF/mips-r-jalr-non-functions.s @@ -0,0 +1,60 @@ +# REQUIRES: mips +## Check that we ignore R_MIPS_JALR relocations agains non-function symbols. +## Older versions of clang was erroneously generating them for function pointers +## loaded from any table (not just the GOT), so we need to ignore the relocations +## to avoid generating binaries that crash when executed. + +## This test case was created from the following C source code: +## int callee(void) { return 0; } +## typedef int (*fnptr)(void); +## static fnptr function_table[] = { &callee, &callee }; +## // to prevent the compiler from optimizing away the table: +## fnptr* get_function_table(void) { return function_table; } +## int __start(int x) { +## return function_table[0](); +## } + +# RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t.o +# RUN: ld.lld -shared %t.o -o %t.so 2>&1 | FileCheck %s -check-prefix WARNING-MESSAGE +# WARNING-MESSAGE: warning: found R_MIPS_JALR relocation against non-function symbol function_table. This is invalid and most likely a compiler bug. +# RUN: llvm-objdump -d %t.so | FileCheck %s + + .set noreorder + .set nomacro + .set noat + .text + + .globl __start + .p2align 3 + .type __start,@function + .ent __start +__start: + ld $1, %got_page(function_table)($gp) + ld $t9, %got_ofst(function_table)($1) +## This relocation should not have been generated by the compiler +## But we should still be able to handle it + .reloc .Ltmp0, R_MIPS_JALR, function_table +.Ltmp0: + jalr $t9 + nop + .end __start +.Lfunc_end2: + .size __start, .Lfunc_end2-__start + + .type function_table,@object + .data + .p2align 3 +function_table: + .8byte 0 + .8byte 0 + .size function_table, 16 + + +# CHECK-LABEL: Disassembly of section .text: +# CHECK-EMPTY: +# CHECK-NEXT: 0000000000010370 __start: +# CHECK-NEXT: 10370: df 81 80 20 ld $1, -32736($gp) +# CHECK-NEXT: 10374: dc 39 03 80 ld $25, 896($1) +## This previously generated a "bal 65560 " which would crash at run time: +# CHECK-NEXT: 10378: 03 20 f8 09 jalr $25 +# CHECK-NEXT: 1037c: 00 00 00 00 nop