bug-binutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Bug ld/21375] MIPS: Non-zero run-time value produced for PIC references


From: cvs-commit at gcc dot gnu.org
Subject: [Bug ld/21375] MIPS: Non-zero run-time value produced for PIC references to undefined hidden or internal weak symbols
Date: Fri, 14 Sep 2018 19:25:40 +0000

https://sourceware.org/bugzilla/show_bug.cgi?id=21375

--- Comment #3 from cvs-commit at gcc dot gnu.org <cvs-commit at gcc dot 
gnu.org> ---
The master branch has been updated by Maciej W. Rozycki <address@hidden>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=47275900adcda29161e2853179c1fbde4da4c86a

commit 47275900adcda29161e2853179c1fbde4da4c86a
Author: Maciej W. Rozycki <address@hidden>
Date:   Fri Sep 14 20:22:56 2018 +0100

    PR ld/21375: MIPS: Fix non-zero run-time value for undefined weaks

    We have an issue in the MIPS backend, with the handling of undefined
    hidden and internal weak symbols.  References to such symbols are
    supposed to resolve to 0 according to the ELF gABI[1]:

    "Unresolved weak symbols have a zero value."

    and the 64-bit MIPS psABI[2]:

    "If a symbol with one of these [hidden or internal] attributes has no
    definition within the executable/DSO being linked, then it must be
    resolved to allocated space if common, resolved to zero if weak, or an
    error reported otherwise."

    however if a GOT relocation is used, then a local GOT entry is created
    and used to satisfy the reference.  Such an entry is then (in DSO and
    PIE binaries) subject to the usual load-time relocation, which means a
    non-zero value will be returned if the base address is non-zero.  This
    will defeat the usual run-time sequence like:

    void a (void) __attribute__ ((visibility ("hidden"), weak));

    void
    x (void)
    {
      if (a)
        a ();
    }

    This can be reproduced with this simple code:

    $ cat libtest.c
    extern int a __attribute__ ((visibility ("hidden"), weak));

    int *
    x (void)
    {
      return &a;
    }
    $ cat test.c

    int *x (void);

    int
    main (void)
    {
      printf ("a: %p\n", x ());

      return 0;
    }
    $ gcc -shared -fPIC -o libtest.so libtest.c
    $ gcc -o test test.c -Wl,-rpath,$(pwd) libtest.so
    $ ./test
    a: 0x77184000
    $

    The usual approach targets take is making all the steps required to
    assign a GOT entry for the symbol referred, and then leave its contents
    at zero with no dynamic relocation attached, therefore ensuring that the
    value does not change at load time.  However this is not going to work
    with the implicitly relocated GOT the MIPS psABI specifies[3]:

    "The dynamic linker relocates the global offset table by first adding
    the difference between the base where the shared object is loaded and
    the value of the dynamic tag DT_MIPS_BASE_ADDRESS to all local global
    offset table entries."

    and we cannot therefore use the local GOT part.

    And we cannot offhand use the global part either, as the symbol would
    then have to be exported and possibly wrongly preempt symbols in other
    modules involved in the dynamic load, because as per the ELF gABI[1] we
    are not allowed to enter a hidden or internal symbol into the dynamic
    symbol table (and then use its associated GOT entry):

    "A hidden symbol contained in a relocatable object must be either
    removed or converted to STB_LOCAL binding by the link-editor when the
    relocatable object is included in an executable file or shared object."

    and:

    "An internal symbol contained in a relocatable object must be either
    removed or converted to STB_LOCAL binding by the link-editor when the
    relocatable object is included in an executable file or shared object."

    So we have to choose something else.

    Our choice is further limited by the need for the reference associated
    with the GOT relocation to stay within the signed 16-bit limit from the
    GOT pointer base register, while being compliant with the ELF gABI and
    the MIPS psABI.  However as Alan Modra has observed[4] one possibility
    is to edit (relax) the code such that the GOT reference is removed
    altogether.

    Based on these observations then modify MIPS BFD linker backend code to:

    1. Interpret code associated with GOT relocations and relax the usual LW
       or LD instructions into a corresponding immediate load operation that
       places the value of 0 in the intended register, while leaving the GOT
       entry allocated and initialized as usually.

    2. Leave any other instructions associated with GOT relocations in place
       and instead redirect the reference to a global GOT entry associated
       with a special `__gnu_absolute_zero' symbol created for this purpose,
       whose value is 0, SHN_ABS section marks it absolute, binding is
       global and export class protected, ensuring that the locally provided
       value is always used at load time, and that the value is not
       relocated by the dynamic loader.

    3. Adjust any high-part GOT relocation used, typically associated with
       a LUI instruction, accordingly, so that run-time consistency is
       maintained, either by resolving to the original entry if the
       instruction associated with the corresponding low-part GOT relocation
       has been relaxed to an immediate load (in which case the value loaded
       with LUI will be overwritten), or by also redirecting the reference
       to `__gnu_absolute_zero' to complete the GOT access sequence if that
       symbol has been used.

    4. Add a target `elf_backend_hide_symbol' hook, for the three MIPS ABIs,
       which prevents the `__gnu_absolute_zero' symbol from being forced
       local, to ensure that the redirection works and the symbol remains
       global/protected with existing linker scripts unchanged.

    5. Observing the issue with handling SHN_ABS symbols in the GNU dynamic
       loader, covered by glibc PR 19818, set the EI_ABIVERSION field in the
       ELF file header produced to 4 (ABI_ABSOLUTE) if `__gnu_absolute_zero'
       symbol has been produced and the target configured indicates the GNU
       operating system, so that broken versions of the GNU dynamic loader
       gracefully reject the file in loading rather than going astray.  Keep
       EI_ABIVERSION at the original value for other operating systems or if
       no `__gnu_absolute_zero' symbol has been made.

    The name of the special `__gnu_absolute_zero' has no meaning other than
    how a human reader can interpret it, as it is ignored in dynamic loading
    in the handling of the scenarios concerned.  This is because the symbol
    resolves locally, and it's only the symbol's attributes that matter so
    that the associated GOT entry remains unchanged at load time.

    Therefore the name is somewhat arbitrary, observing however the need to
    use the name space reserved for the system so that it does not conflict
    with a possible user symbol, and hence the leading underscore, and also
    the `gnu' infix to denote a GNU feature.  Other implementations wishing
    to address the problem in a similar way may choose a different name and
    have the solution still work, possibly with a mixture of modules used in
    a dynamic having symbols of different names provided, which will however
    not interact with each other due to the protected export class.

    The symbol can be referred explicitly, however the name is an internal
    implementation detail rather than a part of the ABI, and therefore no
    specific semantics is guaranteed.

    One limitation of this change is that if `__gnu_absolute_zero' has been
    already defined, then we do not wipe the old definition and all kinds of
    odd behavior can result.  This is however like with other symbols we
    internally define, such as `_GLOBAL_OFFSET_TABLE_' or `__rld_map', and
    therefore left as a possible future enhancement.

    As an optimization the relaxation of LW and LD instructions to a load of
    immediate zero is always made, even SVR4 PIC code for code that will end
    up in a regular (non-PIE) executable, because there is a cache advantage
    with the avoidance of a load from the GOT, even if it is otherwise
    guaranteed to remain zero.  It does not reliably happen though, due to a
    symbol exportation issue affecting executables, covered by PR ld/21805.

    One existing test case needs to be updated, as it triggers relaxation
    introduced with this change and consequently linker output does not
    match expectations anymore.  As we want to keep the original issue
    covered with the test case modify it then to use the LWL instruction in
    place of LW, and adjust the output expected accordingly.

    References:

    [1] "System V Application Binary Interface - DRAFT - 19 October 2010",
        The SCO Group, Section "Symbol Table",
        <http://www.sco.com/developers/gabi/2012-12-31/ch4.symtab.html>

    [2] "64-bit ELF Object File Specification, Draft Version 2.5", MIPS
        Technologies / Silicon Graphics Computer Systems, Order Number
        007-4658-001, Section 2.5 "Symbol Table", p. 22,
       
<http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf>

    [3] "SYSTEM V APPLICATION BINARY INTERFACE, MIPS RISC Processor
        Supplement, 3rd Edition", Section "Global Offset Table", p. 5-10,
        <http://www.linux-mips.org/pub/linux/mips/doc/ABI/mipsabi.pdf>

    [4] "Undo dynamic symbol state after regular object sym type mismatch",
        <https://sourceware.org/ml/binutils/2017-07/msg00265.html>

        bfd/
        PR ld/21375
        * elfxx-mips.h (_bfd_mips_elf_hide_symbol): New prototype.
        (_bfd_mips_elf_linker_flags): Update prototype.
        * elf32-mips.c (elf_backend_hide_symbol): New macro.
        * elf64-mips.c (elf_backend_hide_symbol): Likewise.
        * elfn32-mips.c (elf_backend_hide_symbol): Likewise.
        * elfxx-mips.c (mips_elf_link_hash_table): Add
        `use_absolute_zero' and `gnu_target' members.
        (mips_elf_record_global_got_symbol): Call
        `_bfd_mips_elf_hide_symbol' rather than
        `_bfd_elf_link_hash_hide_symbol'.
        (mips_use_local_got_p): Return FALSE if the symbol is absolute.
        (mips_elf_obtain_contents): Reorder function.
        (mips_elf_nullify_got_load): New function.
        (mips_elf_calculate_relocation): Add `contents' parameter.
        Nullify GOT loads or if it is not possible, then redirect GOT
        relocations to the `__gnu_absolute_zero' symbol, for references
        that are supposed to resolve to zero.
        (mips_elf_define_absolute_zero): New function.
        (_bfd_mips_elf_check_relocs): Prepare for arrangements made in
        `mips_elf_calculate_relocation' for references made via the GOT
        that are supposed to resolve to zero.
        (_bfd_mips_elf_hide_symbol): New function.
        (_bfd_mips_elf_linker_flags): Add the `gnu_target' parameter,
        set the `gnu_target' member of the MIPS hash table.
        (MIPS_LIBC_ABI_ABSOLUTE): New enumeration constant.
        (_bfd_mips_post_process_headers): Use it.

        ld/
        PR ld/21375
        * emultempl/mipself.em: Set `gnu_target' according to ${target}.
        (mips_create_output_section_statements): Update call to
        `_bfd_mips_elf_linker_flags'.
        * testsuite/ld-mips-elf/pr21334.s: Use LWL rather than LW.
        * testsuite/ld-mips-elf/pr21334.dd: Update accordingly.

-- 
You are receiving this mail because:
You are on the CC list for the bug.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]