Bug 14718 - ld crashes on ARMv5 due to unaligned memory access
Summary: ld crashes on ARMv5 due to unaligned memory access
Status: NEW
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.22
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL: https://bugs.gentoo.org/433669
Keywords:
Depends on:
Blocks:
 
Reported: 2012-10-14 14:14 UTC by Ambroz Bizjak
Modified: 2012-10-14 22:50 UTC (History)
2 users (show)

See Also:
Host:
Target: armv5-linux-gnueabi
Build:
Last reconfirmed:


Attachments
hackish fix (1.71 KB, patch)
2012-10-14 14:14 UTC, Ambroz Bizjak
Details | Diff
non-hackish fix (857 bytes, patch)
2012-10-14 20:17 UTC, Ambroz Bizjak
Details | Diff
non-hackish fix, fixed (970 bytes, patch)
2012-10-14 21:16 UTC, Ambroz Bizjak
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ambroz Bizjak 2012-10-14 14:14:05 UTC
Created attachment 6686 [details]
hackish fix

See Gentoo bug: https://bugs.gentoo.org/show_bug.cgi?id=433669

With binutils 2.22, gcc 4.5.4 on an ARMv5 system, ld crashes when static linking a program:

# gcc -static hello.c
collect2: ld terminated with signal 11 [Segmentation fault]

The input can be anything, like "int main () { return 0; }".

I've tracked it down to misaligned memory accesses in bfd/elf32-arm.c. The attached patch fixes the misaligned access, the crash no longer occurs and the resulting program runs.

Note that this patch uses a gcc-only feature (attribute packed which makes the compiler handle misalignment in software). It's probably not suitable for inclusion into binutils as-is if binutils is to compile with something other than gcc. Instead of this, memcpy() can be used to read and write misaligned memory in a standard way.
Comment 1 Andreas Schwab 2012-10-14 17:08:37 UTC
Why is objalloc_alloc returning unaligned memory?
Comment 2 Ambroz Bizjak 2012-10-14 20:17:01 UTC
Created attachment 6687 [details]
non-hackish fix

This fixes the misalignment by properly aligning pointers. Note that it is only correct if the layout of the structure allocated in elf32_arm_allocate_local_sym_info() is irrelevant, and this seems to be the case. Tested, compiles and runs a hello world.

Note: Yes, the fix is actually correct, at least according to the C11 standard. It is a consequence of the standard that the alignment of a type always divides its size (and this makes an object aligned to its size correctly aligned). If that was not the case, the second element of an array of such objects could be misaligned even when the first element is aligned.

But note that there seem to be other instances of misaligned access involving bfd_vma* and bfd_signed_vma* which were not triggered by what I have tested and may be for architectures other than ARM.
Comment 3 Ambroz Bizjak 2012-10-14 20:28:34 UTC
Andreas: the allocation function (bfd_zalloc, not objalloc_alloc) is not returning unaligned memory. The problem is that the code is trying to manually place multiple arrays into a single memory block, without making sure the extra (non-first) arrays are aligned.
Comment 4 Andreas Schwab 2012-10-14 20:34:17 UTC
You cannot pass a pointer for an integral argument.  How did you manage to
suppress the warning?

It'll be simpler to just allocate each array separately.
Comment 5 Siarhei Siamashka 2012-10-14 20:57:41 UTC
Or maybe changing the order like this:

diff --git a/bfd/elf32-arm.c b/bfd/elf32-arm.c
index 633bb64..0efcf1d 100644
--- a/bfd/elf32-arm.c
+++ b/bfd/elf32-arm.c
@@ -3061,12 +3061,12 @@ elf32_arm_allocate_local_sym_info (bfd *abfd)
       elf_local_got_refcounts (abfd) = (bfd_signed_vma *) data;
       data += num_syms * sizeof (bfd_signed_vma);
 
-      elf32_arm_local_iplt (abfd) = (struct arm_local_iplt_info **) data;
-      data += num_syms * sizeof (struct arm_local_iplt_info *);
-
       elf32_arm_local_tlsdesc_gotent (abfd) = (bfd_vma *) data;
       data += num_syms * sizeof (bfd_vma);
 
+      elf32_arm_local_iplt (abfd) = (struct arm_local_iplt_info **) data;
+      data += num_syms * sizeof (struct arm_local_iplt_info *);
+
       elf32_arm_local_got_tls_type (abfd) = data;
     }
   return TRUE;

Because in the current code bfd_signed_vma array (64-bit elements) is followed by arm_local_iplt_info * array (32-bit elements) and then followed by bfd_vma array (64-bit elements again). If num_syms is odd, then the last array is not 64-bit aligned. Just swapping the order of the second and third arrays might help.
Comment 6 Ambroz Bizjak 2012-10-14 21:16:50 UTC
Created attachment 6688 [details]
non-hackish fix, fixed

oops, that was indeed casting pointers to size_t and back :)
This fixes it by working with offsets from the base of the malloc block.

I would be very careful with swapping because the size of the arm_local_iplt_info* is unknown (could be 64-bit or anything). It would still work, but just by accident, because we would allocate 2*num_syms of 32-bit objects first, making sure the 64-bit pointers that follow are aligned. So I strongly prefer the manual alignment approach; it's unlikely that a small change in the code would break it.
Comment 7 Siarhei Siamashka 2012-10-14 22:32:12 UTC
(In reply to comment #5)
> Because in the current code bfd_signed_vma array (64-bit elements) is followed
> by arm_local_iplt_info * array (32-bit elements) and then followed by bfd_vma
> array (64-bit elements again). If num_syms is odd, then the last array is not
> 64-bit aligned. Just swapping the order of the second and third arrays might
> help.

Hmm, this is beginning to be really funny. My gentoo arm system has "#define BFD_ARCH_SIZE 64" line in /usr/include/bfd.h, resulting in 64-bit vma typedefs. I guess such weird configuration is a prerequisite for triggering this alignment issue. The culprit is gentoo toolchain-binutils.eclass which is forcing --enable-64-bit-bfd configure option. The architectures like ARM surely have lots of spare RAM to waste... Looks like at least this part of the problem needs to be solved on gentoo side.
Comment 8 Ambroz Bizjak 2012-10-14 22:49:24 UTC
(In reply to comment #7)
> Hmm, this is beginning to be really funny. My gentoo arm system has "#define
> BFD_ARCH_SIZE 64" line in /usr/include/bfd.h, resulting in 64-bit vma typedefs.
> I guess such weird configuration is a prerequisite for triggering this
> alignment issue. The culprit is gentoo toolchain-binutils.eclass which is
> forcing --enable-64-bit-bfd configure option. The architectures like ARM surely
> have lots of spare RAM to waste... Looks like at least this part of the problem
> needs to be solved on gentoo side.

Yeah, that seems to have triggered it, but the bug is still entirely in binutils. Even if you don't pass --enable-64-bit-bfd for ARM toolchain, this will still happen for an ARM->x86_64 cross toolchain (for whatever reason someone would need that).
Comment 9 Siarhei Siamashka 2012-10-14 22:50:44 UTC
(In reply to comment #6)
> I would be very careful with swapping because the size of the
> arm_local_iplt_info* is unknown (could be 64-bit or anything). It would still
> work, but just by accident, because we would allocate 2*num_syms of 32-bit
> objects first, making sure the 64-bit pointers that follow are aligned.

Thanks for pointing this. Indeed, the sizes of vma typedefs and the sizes of pointers can make some really weird combinations.

> So I strongly prefer the manual alignment approach; it's unlikely that
> a small change in the code would break it.

This or the suggestion from Andreas Schwab with separate allocations.

One nitpick about your patch is the unnecessary realignment for the char array (sizeof(char) is always 1).