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.
Why is objalloc_alloc returning unaligned memory?
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.
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.
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.
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.
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.
(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.
(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).
(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).