Merge tag 'u-boot-atmel-fixes-2020.07-a' of https://gitlab.denx.de/u-boot/custodians...
[oweals/u-boot.git] / arch / arc / lib / relocate.c
index 96b4bd3d8fa6d1a2c83646b0ee7d67a695e821e8..6882ebec6d883d5ba69e333dcf27e98a45a01ad3 100644 (file)
@@ -1,15 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2013-2014 Synopsys, Inc. All rights reserved.
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <elf.h>
+#include <log.h>
 #include <asm-generic/sections.h>
 
 extern ulong __image_copy_start;
+extern ulong __ivt_start;
 extern ulong __ivt_end;
+extern ulong __text_end;
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -49,7 +51,7 @@ int do_elf_reloc_fixups(void)
        debug("Section .rela.dyn is located at %08x-%08x\n",
              (unsigned int)re_src, (unsigned int)re_end);
 
-       Elf32_Addr *offset_ptr_rom, *last_offset = NULL;
+       Elf32_Addr *offset_ptr_rom;
        Elf32_Addr *offset_ptr_ram;
 
        do {
@@ -58,15 +60,28 @@ int do_elf_reloc_fixups(void)
 
                /* Check that the location of the relocation is in .text */
                if (offset_ptr_rom >= (Elf32_Addr *)&__image_copy_start &&
-                   offset_ptr_rom > last_offset) {
-                       unsigned int val;
+                   offset_ptr_rom < (Elf32_Addr *)&__image_copy_end) {
+                       unsigned int val, do_swap = 0;
                        /* Switch to the in-RAM version */
                        offset_ptr_ram = (Elf32_Addr *)((ulong)offset_ptr_rom +
                                                        gd->reloc_off);
 
-                       debug("Patching value @ %08x (relocated to %08x)\n",
+#ifdef __LITTLE_ENDIAN__
+                       /* If location in ".text" section swap value */
+                       if (((u32)offset_ptr_rom >= (u32)&__text_start &&
+                            (u32)offset_ptr_rom <= (u32)&__text_end)
+#if defined(__ARC700__) || defined(__ARC600__)
+                           || ((u32)offset_ptr_rom >= (u32)&__ivt_start &&
+                               (u32)offset_ptr_rom <= (u32)&__ivt_end)
+#endif
+                          )
+                               do_swap = 1;
+#endif
+
+                       debug("Patching value @ %08x (relocated to %08x)%s\n",
                              (unsigned int)offset_ptr_rom,
-                             (unsigned int)offset_ptr_ram);
+                             (unsigned int)offset_ptr_ram,
+                             do_swap ? ", middle-endian encoded" : "");
 
                        /*
                         * Use "memcpy" because target location might be
@@ -76,28 +91,45 @@ int do_elf_reloc_fixups(void)
                         */
                        memcpy(&val, offset_ptr_ram, sizeof(int));
 
-#ifdef __LITTLE_ENDIAN__
-                       /* If location in ".text" section swap value */
-                       if ((unsigned int)offset_ptr_rom <
-                           (unsigned int)&__ivt_end)
+                       if (do_swap)
                                val = (val << 16) | (val >> 16);
-#endif
 
                        /* Check that the target points into executable */
-                       if (val >= (unsigned int)&__image_copy_start && val <=
-                           (unsigned int)&__image_copy_end) {
-                               val += gd->reloc_off;
-#ifdef __LITTLE_ENDIAN__
-                               /* If location in ".text" section swap value */
-                               if ((unsigned int)offset_ptr_rom <
-                                   (unsigned int)&__ivt_end)
-                                       val = (val << 16) | (val >> 16);
-#endif
-                               memcpy(offset_ptr_ram, &val, sizeof(int));
+                       if (val < (unsigned int)&__image_copy_start ||
+                           val > (unsigned int)&__image_copy_end) {
+                               /* TODO: Use panic() instead of debug()
+                                *
+                                * For some reason GCC might generate
+                                * fake relocation even for LD/SC of constant
+                                * inderectly. See an example below:
+                                * ----------------------->8--------------------
+                                * static int setup_mon_len(void)
+                                * {
+                                *         gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
+                                *         return 0;
+                                * }
+                                * ----------------------->8--------------------
+                                *
+                                * And that's what we get in the binary:
+                                * ----------------------->8--------------------
+                                * 10005cb4 <setup_mon_len>:
+                                * 10005cb4:       193c 3f80 0003 2f80     st      0x32f80,[r25,60]
+                                *                         10005cb8: R_ARC_RELATIVE        *ABS*-0x10000000
+                                * 10005cbc:       7fe0                    j_s.d   [blink]
+                                * 10005cbe:       700c                    mov_s   r0,0
+                                * ----------------------->8--------------------
+                                */
+                               debug("Relocation target %08x points outside of image\n",
+                                     val);
                        }
-               }
-               last_offset = offset_ptr_rom;
 
+                       val += gd->reloc_off;
+
+                       if (do_swap)
+                               val = (val << 16) | (val >> 16);
+
+                       memcpy(offset_ptr_ram, &val, sizeof(int));
+               }
        } while (++re_src < re_end);
 
        return 0;