From 160eba9301df423116377113f2d4189d785c7dde Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 21 Feb 2019 17:41:47 +1030 Subject: [PATCH] PowerPC __tls_get_addr arg parsing The syntax we ended up with for -m32 -fPIC calls to __tls_get_addr is rather weird. bl __tls_get_addr+0x8000(gd0@tlsgd)@plt This came about by accident, probably due to requiring the arg reloc before the call reloc. Of course the @plt really belongs with __tls_get_addr since it affects the call rather than the call arg, and it isn't a great deal of trouble to ensure the relocs are emitted in the correct order. This patch supports a newer syntax, like so: bl __tls_get_addr+0x8000@plt(gd0@tlsgd) gas/ * config/tc-ppc.c (parse_tls_arg): New function, extracted.. (md_assembler): ..from here. Call it after parsing other suffix modifiers too. ld/ * testsuite/ld-powerpc/tls32.s: Test new @plt syntax. --- gas/config/tc-ppc.c | 92 +++++++++++++++++++-------------- ld/testsuite/ld-powerpc/tls32.s | 3 +- 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index e8deb32882c..35d85a4520c 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -2999,6 +2999,43 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative) return size; } +/* If we have parsed a call to __tls_get_addr, parse an argument like + (gd0@tlsgd). *STR is the leading parenthesis on entry. If an arg + is successfully parsed, *STR is updated past the trailing + parenthesis and trailing white space, and *TLS_FIX contains the + reloc and arg expression. */ + +static int +parse_tls_arg (char **str, const expressionS *exp, struct ppc_fixup *tls_fix) +{ + const char *sym_name = S_GET_NAME (exp->X_add_symbol); + if (sym_name[0] == '.') + ++sym_name; + + tls_fix->reloc = BFD_RELOC_NONE; + if (strcasecmp (sym_name, "__tls_get_addr") == 0) + { + char *hold = input_line_pointer; + input_line_pointer = *str + 1; + expression (&tls_fix->exp); + if (tls_fix->exp.X_op == O_symbol) + { + if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0) + tls_fix->reloc = BFD_RELOC_PPC_TLSGD; + else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0) + tls_fix->reloc = BFD_RELOC_PPC_TLSLD; + if (tls_fix->reloc != BFD_RELOC_NONE) + { + input_line_pointer += 7; + SKIP_WHITESPACE (); + *str = input_line_pointer; + } + } + input_line_pointer = hold; + } + return tls_fix->reloc != BFD_RELOC_NONE; +} + /* This routine is called for each instruction to be assembled. */ void @@ -3388,47 +3425,12 @@ md_assemble (char *str) { bfd_reloc_code_real_type reloc = BFD_RELOC_NONE; #ifdef OBJ_ELF - if (ex.X_op == O_symbol && str[0] == '(') + /* Look for a __tls_get_addr arg using the insane old syntax. */ + if (ex.X_op == O_symbol && *str == '(' && fc < MAX_INSN_FIXUPS + && parse_tls_arg (&str, &ex, &fixups[fc])) { - const char *sym_name = S_GET_NAME (ex.X_add_symbol); - if (sym_name[0] == '.') - ++sym_name; - - if (strcasecmp (sym_name, "__tls_get_addr") == 0) - { - expressionS tls_exp; - - hold = input_line_pointer; - input_line_pointer = str + 1; - expression (&tls_exp); - if (tls_exp.X_op == O_symbol) - { - reloc = BFD_RELOC_NONE; - if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0) - { - reloc = BFD_RELOC_PPC_TLSGD; - input_line_pointer += 7; - } - else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0) - { - reloc = BFD_RELOC_PPC_TLSLD; - input_line_pointer += 7; - } - if (reloc != BFD_RELOC_NONE) - { - SKIP_WHITESPACE (); - str = input_line_pointer; - - if (fc >= MAX_INSN_FIXUPS) - as_fatal (_("too many fixups")); - fixups[fc].exp = tls_exp; - fixups[fc].opindex = *opindex_ptr; - fixups[fc].reloc = reloc; - ++fc; - } - } - input_line_pointer = hold; - } + fixups[fc].opindex = *opindex_ptr; + ++fc; } if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE) @@ -3703,6 +3705,16 @@ md_assemble (char *str) break; } } + + /* Look for a __tls_get_addr arg after any __tls_get_addr + modifiers like @plt. This fixup must be emitted before + the usual call fixup. */ + if (ex.X_op == O_symbol && *str == '(' && fc < MAX_INSN_FIXUPS + && parse_tls_arg (&str, &ex, &fixups[fc])) + { + fixups[fc].opindex = *opindex_ptr; + ++fc; + } #endif /* We need to generate a fixup for this expression. */ diff --git a/ld/testsuite/ld-powerpc/tls32.s b/ld/testsuite/ld-powerpc/tls32.s index 1c7a890402f..2893ad2103a 100644 --- a/ld/testsuite/ld-powerpc/tls32.s +++ b/ld/testsuite/ld-powerpc/tls32.s @@ -62,7 +62,8 @@ _start: #LD addi 3,31,ld0@got@tlsld #R_PPC_GOT_TLSLD16 ld0 .ifdef TLSMARK - bl __tls_get_addr+0x8000(ld0@tlsld)@plt #R_PPC_TLSLD ld0 +#exercise saner new syntax with @plt before the arg + bl __tls_get_addr+0x8000@plt(ld0@tlsld) #R_PPC_TLSLD ld0 #R_PPC_PLTREL24 __tls_get_addr+0x8000 .else bl __tls_get_addr+0x8000@plt #R_PPC_PLTREL24 __tls_get_addr+0x8000 -- 2.30.2