[armel] gcc computes wrong address for main() at build time

Bug #721531 reported by Michael Casadevall on 2011-02-18
18
This bug affects 2 people
Affects Status Importance Assigned to Milestone
Linaro GCC
Fix Released
Medium
Richard Sandiford
4.5
Fix Released
Medium
Richard Sandiford
4.6
Fix Released
Medium
Richard Sandiford
gcc
Fix Released
Medium
gcc-4.5 (Ubuntu)
High
Linaro Tool Chain Working Group
Natty
High
Linaro Tool Chain Working Group
Oneiric
High
Linaro Tool Chain Working Group
gcc-4.6 (Ubuntu)
Undecided
Unassigned
Natty
Undecided
Unassigned
Oneiric
Undecided
Unassigned

Bug Description

Binary package hint: gcc-4.5

During the debugging of mono, we found a confirmed tool chain regression and isolated it down to a testcase:

Test code:

void main() {
        void *p = main;
        if ((int)p & 1) printf ("HIT!\n");
}

Output:

mcasadevall@risingsun:~/tmp$ gcc -g test.c
test.c: In function 'main':
test.c:3:18: warning: incompatible implicit declaration of built-in function 'printf'
mcasadevall@risingsun:~/tmp$ ./a.out
HIT!

mcasadevall@risingsun:~/tmp$ gcc -g -O2 test.c
test.c: In function 'main':
test.c:3:18: warning: incompatible implicit declaration of built-in function 'printf'
mcasadevall@risingsun:~/tmp$ ./a.out
mcasadevall@risingsun:~/tmp$

Doesn't occur with gcc-4.4.

Additional comments from #monodev:
16:26:58 < vargaz> NCommander: it seems to think function addresses on arm
                   have their lowest bit set to 0, which is not true for
                   thumb.

Related branches

Amendment: Also occurs on amd64

mcasadevall@daybreak:/tmp$ uname -a
Linux daybreak 2.6.38-2-generic #29-Ubuntu SMP Fri Feb 4 13:03:04 UTC 2011 x86_64 x86_64 x86_64 GNU/Linux
mcasadevall@daybreak:/tmp$ gcc -O2 test.c
test.c: In function ‘main’:
test.c:3:13: warning: cast from pointer to integer of different size
test.c:3:25: warning: incompatible implicit declaration of built-in function ‘printf’

Disregard it affecting on x86, false-positive as it also still occurs with -O0 on that platform.

tags: added: armel
Changed in gcc-4.5 (Ubuntu):
status: New → Confirmed
importance: Undecided → High
summary: [armel] Confirmed toolchain regression when using & in an if statement
+ on a function

Here's what gdb shows:

Breakpoint 1, main () at testcase.c:4
4 void *p = main;
(gdb) disas
Dump of assembler code for function main:
   0x00008390 <+0>: push {r7, lr}
   0x00008392 <+2>: sub sp, #8
   0x00008394 <+4>: add r7, sp, #0
=> 0x00008396 <+6>: movw r3, #33681 ; 0x8391
   0x0000839a <+10>: movt r3, #0
   0x0000839e <+14>: str r3, [r7, #4]
   0x000083a0 <+16>: ldr r3, [r7, #4]
   0x000083a2 <+18>: and.w r3, r3, #1
   0x000083a6 <+22>: uxtb r3, r3
   0x000083a8 <+24>: cmp r3, #0
   0x000083aa <+26>: beq.n 0x83b8 <main+40>
   0x000083ac <+28>: movw r0, #33808 ; 0x8410
   0x000083b0 <+32>: movt r0, #0
   0x000083b4 <+36>: blx 0x8304 <puts>
   0x000083b8 <+40>: add.w r7, r7, #8
   0x000083bc <+44>: mov sp, r7
   0x000083be <+46>: pop {r7, pc}
End of assembler dump.

Steve Langasek (vorlon) wrote :

So the issue isn't that & is working incorrectly, the issue is that the address of main according to gcc is given as 0x8391 and this seems to not be what we want. (gdb has a different idea of main's address, for instance - 0x8396.)

Steve Langasek (vorlon) wrote :

This is reproducible with gcc-4.5 4.5.1-8ubuntu2, the version being used at natty open. So it seems to be a longstanding bug.

Not reproducible when building with -O1 or -O2 because gcc optimizes the code out completely (getting the right answer for the address of main in this case... or at least the right parity).

Not reproducible when building with -marm.

summary: - [armel] Confirmed toolchain regression when using & in an if statement
- on a function
+ [armel] gcc computes wrong address for main() at build time
Steve Langasek (vorlon) on 2011-02-18
Changed in gcc-4.5 (Ubuntu Natty):
status: Confirmed → Triaged
Changed in gcc-linaro:
status: New → Confirmed
Paul Larson (pwlars) wrote :

Not sure if this is relevant, but I seem to be seeing something slightly different. I also added a printf to see what it thinks the value of p is, and tested this with gcc-4.3, 4.4, 4.5:

gcc version 4.3.5 (Ubuntu 4.3.5-3ubuntu1)
without -O2:
p=0x83e0
with -O2:
p=0x83e0

gcc version 4.4.5 (Ubuntu/Linaro 4.4.5-11ubuntu1)
without -O2:
HIT!
p=0x83c1
with -O2:
HIT!
p=0x83c5

gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-3ubuntu1)
without -O2:
HIT!
p=0x83c1
with -O2:
p=0x8399

gcc-4.3 was the only consistent one, but it did not have -mthumb enabled by default.
with gcc-4.3 -mthumb, I get the same thing with or without using -O2:
HIT!
p=0x83e1

Michael Hope (michaelh1) wrote :

@paul: the address will be changing due to the code before it changing at different optimisation levels. The important thing is that in ARM mode the LSB is clear, while in Thumb mode the LSB is set in all situations.

Michael Hope (michaelh1) wrote :

@steve: GDB has set the breakpoint at the first interesting instruction after the prologue, which is fine. GCC seems to be correct - if you want to jump to main, you want to jump to address 0x8390 in Thumb mode (i.e. LSB set).

Michael Hope (michaelh1) wrote :

Confirmed in Linaro GCC 4.5-2011.02-0. I assume that GCC is looking at the type, checking the expected alignment, and then inferring what the value of the LSB should be. This code:

extern int foo;

void main()
{
           void *p = &foo;
           if ((int)p & 1) printf ("HIT!\n");
}

gets optimised away into a bx lr as an int should be word aligned, so the lower two bits should be zero. This code:

extern char foo;

void main()
{
           void *p = &foo;
           if ((int)p & 1) printf ("HIT!\n");
}

doesn't get optimised away as foo could be at an odd address.

I can't remember if casting a function pointer to an int is undefined behaviour. I think it is, so this code is invalid. However it is very useful so I think we should support it.

Changed in gcc-linaro:
status: Confirmed → Triaged
importance: Undecided → Medium

@Steve: if you want GDB to give the true function address use '*main' - 'main' will give the first address after the prologue (at least when setting breakpoints, it does).

@Michael: I think casting pointers from one pointer type to another (type punning) is deeply dodgy due to strict-aliasing rules, but I don't think converting them to integers is a problem (provided they are the same width).

The bug here appears to be that GCC assumes that function pointer are always aligned, but fails to realise that thumb function pointers are always aligned+1, so to speak. I don't think it will be too hard to fix it.

(Of course, fixing GCC so it's consistent probably won't help mono - that'll still need a fix. In my experience, this concept of off-by-one pointers breaks many, many things in 'interesting' ways.)

Michael Hope (michaelh1) wrote :

I'm fairly sure this exists in FSF 4.5 but will confirm. FSF bugs are are lower priority to us and we're a bit swamped at the moment so I'm afraid this may not be fixed soon.

A work around would be to break GCC's knowledge of the alignment of the variable. Passing it through another function would do it, but there's probably a better way too.

Changed in gcc-4.5 (Ubuntu Natty):
assignee: nobody → Linaro Tool Chain Working Group (linaro-toolchain-wg)
Kate Stewart (kate.stewart) wrote :

Changing bug to Won't Fix for Natty and retargetting to Oneiric. If this can not be worked around, please update status back to confirmed, and will work with teams to see what options are possible.

Changed in gcc-4.5 (Ubuntu Oneiric):
status: New → Triaged
importance: Undecided → High
Changed in gcc-4.5 (Ubuntu Natty):
status: Triaged → Won't Fix
Ken Werner (kwerner) wrote :

I see a very similar issue even with -O0 when using GCC 4.5.2-8ubuntu1 on my ARM board.

Here is a simple test:
<snip>
$ cat test.c && gcc -O0 -Wall -Winline test.c -o test.bin && echo "O0:" && ./test.bin && gcc -O2 -Wall -Winline test.c -o test.bin && echo "O2:" && ./test.bin

#include <stdio.h>

static long __attribute__ ((noinline))
getfp (void *p)
{
  return (long) p;
}

static void __attribute__ ((noinline))
myfunc(long fp, long foo, long bar, long code, long thumb, long thumb2)
{
  printf ("\tfp: 0x%lx\n", fp);
  printf ("\tfoo: 0x%lx\n", foo);
  printf ("\tbar: 0x%lx\n", bar);
  printf ("\tcode: 0x%lx\n", code);
  printf ("\tthumb: 0x%lx\n", thumb);
  printf ("\tthumb2: 0x%lx\n", thumb2);
}

int main()
{
  long fp = (long) myfunc;
  long foo = fp & 0x1L;
  long bar = getfp (myfunc);
  long code = (long) myfunc & ~0x1L;
  long thumb = (long) myfunc & 0x1L;
  long thumb2 = getfp (myfunc) & 0x1L;
  myfunc(fp, foo, bar, code, thumb, thumb2);
  return 0;
}

O0:
        fp: 0x83b1
        foo: 0x1
        bar: 0x83b1
        code: 0x83b0
        thumb: 0x0
        thumb2: 0x1
O2:
        fp: 0x83a1
        foo: 0x0
        bar: 0x83a1
        code: 0x83a0
        thumb: 0x0
        thumb2: 0x1
</snip>

The interesting thing is the " (long) myfunc & 0x1L".

Ken Werner (kwerner) wrote :

As shown above this bug makes it a bit difficult to obtain the thumb marker.

Michael Hope (michaelh1) wrote :

Hi Ken. I'm not surprised by the results above. At all optimisation levels, GCC knows the alignment of a variable and therefore knows the value of the lower bits. At -O0 'foo' works as this information isn't propagated to the next line. 'thumb' fails as the value and alignment are available right there in the expression.

-O2 is as expected. 'foo' is different as the value propagates to the fp & 0x1L expression.

Note that the work-around of pushing the value through the getfp() function works correctly.

GCC is still wrong though...

Kate Stewart (kate.stewart) wrote :

Is this still an issue with 4.6? Need to understand implications of this for the release please.

Changed in gcc-4.5 (Ubuntu Oneiric):
assignee: nobody → Linaro Tool Chain Working Group (linaro-toolchain-wg)
Michael Hope (michaelh1) wrote :

This fault exists in all upstream versions from 4.5 onwards including recent trunk:

michaelh@ursa2:~/linaro/bugs$ uname -a
Linux ursa2 2.6.35.3-cbuild2+ #8 SMP Mon Apr 4 12:46:46 NZST 2011 armv7l GNU/Linux

michaelh@ursa2:~/linaro/bugs$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 10.10
Release: 10.10
Codename: maverick

michaelh@ursa2:~/linaro/bugs$ /tools/toolchains/gcc-4.4.5-armv7l-maverick-cbuild93-ursa2-cortexa8r1/bin/gcc -O2 align.c ; ./a.out
HIT!

michaelh@ursa2:~/linaro/bugs$ /tools/toolchains/gcc-4.5.2-armv7l-maverick-cbuild93-ursa1-cortexa8r1/bin/gcc -O2 align.c ; ./a.out
(nothing)

michaelh@ursa2:~/linaro/bugs$ /tools/toolchains/gcc-4.6.0-armv7l-maverick-cbuild93-ursa1-cortexa8r1/bin/gcc -O2 align.c ; ./a.out
(nothing)

michaelh@ursa2:~/linaro/bugs$ ~/linaro/toolchains/gcc-4.7~svn174044-armv7l-maverick-cbuild120-ursa4-cortexa9r1/bin/gcc -O2 align.c ; ./a.out
(nothing)

ARM devices encode the instruction set mode in the LSB of the function address. Functions are word aligned on ARM. If you try to test the LSB of a function pointer then GCC assumes that the two least significant bits are zero and optimises away the test.

This problem is seen in Mono and was originally reported at:
 https://bugs.launchpad.net/ubuntu/+source/gcc-4.5/+bug/721531

A reduced test case is:

void main() {
        void *p = main;
        if ((int)p & 1) printf ("HIT!\n");
}

When compiled with -march=armv7-a -mthumb -O0 then the word 'HIT!' will show. When compiled with -O2, the branch is not taken.

The problem does not occur in 4.4.5. It does occur in 4.5.2, 4.6.0, and trunk r174044.

Michael Hope (michaelh1) wrote :

I've logged this in GCC bugzilla. We'll get it fixed up there.

Changed in gcc:
importance: Unknown → Medium
status: Unknown → New
In , Mikpe (mikpe) wrote :

Try passing the function pointer through an opaque identity transform:

    asm("" : "=r"(p) : "0"(main));
    if ((uintptr_t)p & 1) /* do thumb case */

There are several places in the compiler where we assume DECL_ALIGN
constraints the lower bits of the address of the DECL.

See several similar bugs in the past (PR47239 comes to my mind).

fold-const.c:get_pointer_modulus_and_residue looks suspicious to me here,
so you might want to try

Index: gcc/fold-const.c
===================================================================
--- gcc/fold-const.c (revision 174266)
+++ gcc/fold-const.c (working copy)
@@ -9219,7 +9219,8 @@ get_pointer_modulus_and_residue (tree ex
   *residue = 0;

   code = TREE_CODE (expr);
- if (code == ADDR_EXPR)
+ if (code == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (expr, 0)) != FUNCTION_DECL)
     {
       unsigned int bitalign;
       bitalign = get_object_alignment_1 (TREE_OPERAND (expr, 0), residue);

Btw, we finally should introduce a target hook for this I think.

(In reply to comment #3)
> Btw, we finally should introduce a target hook for this I think.

Thanks for the patch in comment #2. How strongly do you feel
about the hook though? In PR35705, it sounded like a lot of
targets actually need an opt-out for functions, either because
of ISA encoding (ARM, MIPS, SH) or because of function
descriptors (IA-64, PA, PPC).

I notice that ARM and mcore also have optimisation-dependent
FUNCTION_BOUNDARYs. Arguably (very arguably) that's a bug,
and they should be using align_functions instead. But if we
make a deliberate decision to honour DECL_ALIGN on functions,
then FUNCTION_BOUNDARY really will be an ABI property.
I'm just worried that the combination of that and the need
to identify exactly which targets should define the hook
might be more hassle than the optimisation is worth.

You said in that bug that masking function addresses isn't
likely to be a common operation, and TBH, I still agree.

Richard

Michael Hope (michaelh1) on 2011-06-09
Changed in gcc-linaro:
assignee: nobody → Richard Sandiford (rsandifo)

Author: rsandifo
Date: Mon Jun 27 09:33:06 2011
New Revision: 175427

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=175427
Log:
gcc/
2011-07-24 Richard Guenther <email address hidden>

 PR tree-optimization/49169
 * fold-const.c (get_pointer_modulus_and_residue): Don't rely on
 the alignment of function decls.

gcc/testsuite/
2011-07-24 Michael Hope <email address hidden>
     Richard Sandiford <email address hidden>

 PR tree-optimization/49169
 * gcc.dg/torture/pr49169.c: New test.

Added:
    trunk/gcc/testsuite/gcc.dg/torture/pr49169.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/fold-const.c
    trunk/gcc/testsuite/ChangeLog

Michael Hope (michaelh1) on 2011-07-04
Changed in gcc-linaro:
status: Triaged → In Progress
Michael Hope (michaelh1) on 2011-07-04
Changed in gcc-linaro:
milestone: none → 4.6-2011.07
Changed in gcc:
status: New → Confirmed
Matthias Klose (doko) on 2011-07-14
Changed in gcc-4.6 (Ubuntu Natty):
status: New → Invalid
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package gcc-4.6 - 4.6.1-4ubuntu1

---------------
gcc-4.6 (4.6.1-4ubuntu1) oneiric; urgency=low

  * Merge with Debian.

gcc-4.6 (4.6.1-4) unstable; urgency=low

  * Update to SVN 20110714 (r176280) from the gcc-4_6-branch.
    - Fix PR tree-optimization/49094, PR target/39633, PR c++/49672,
      PR fortran/49698, PR fortran/49690, PR fortran/49562, PR libfortran/49296,
      PR target/49487, PR tree-optimization/49651, PR ada/48711.

  [ Matthias Klose ]
  * Build Go on alpha for gcc-snapshot builds.
  * For multicore ARM, clear both caches, not just the dcache (proposed
    patch by Andrew Haley).
  * Fix for PR rtl-optimization/{48830,48808,48792}, taken from the trunk.
    LP: #807573.
  * Fix PR tree-optimization/49169, optimisations strip the Thumb/ARM mode bit
    off function pointers (Richard Sandiford). LP: #721531.

  [ Marcin Juszkiewicz ]
  * Define DEB_TARGET_MULTIARCH macro.
  * debian/rules2: Macro and configuration consolidation.

gcc-4.6 (4.6.1-3) unstable; urgency=medium

  * Update to SVN 20110709 (r176108) from the gcc-4_6-branch.
    - Fix PR target/49335, PR tree-optimization/49618, PR c++/49598,
      PR fortran/49479, PR target/49621, PR target/46779, PR target/49660,
      PR c/49644, PR debug/49522, PR debug/49522, PR middle-end/49640,
      PR c++/48157, PR c/49644, PR fortran/48926.
    - Apparently fixes a boost issue. Closes: #632938.
  * Apply proposed patch for PR fortran/49690. Closes: #631204.

  * README.Debian: New section 'Former and/or inactive maintainers'.
 -- Matthias Klose <email address hidden> Thu, 14 Jul 2011 20:18:27 +0200

Changed in gcc-4.6 (Ubuntu Oneiric):
status: New → Fix Released
Matthias Klose (doko) wrote :

fixed in oneiric in gcc-4.5

Changed in gcc-4.5 (Ubuntu Oneiric):
status: Triaged → Fix Released
In , Jye2 (jye2) wrote :

Author: jye2
Date: Mon Sep 19 08:13:02 2011
New Revision: 178955

URL: http://gcc.gnu.org/viewcvs?root=gcc&view=rev&rev=178955
Log:
2011-09-19 Jiangning Liu <email address hidden>

 Backport r175427 from mainline
 2011-06-27 Richard Guenther <email address hidden>

 PR tree-optimization/49169
 * fold-const.c (get_pointer_modulus_and_residue): Don't rely on
 the alignment of function decls.

2011-09-19 Jiangning Liu <email address hidden>

 Backport r175208 from mainline
 2011-06-20 Ramana Radhakrishnan <email address hidden>

 PR target/49385
 * config/arm/thumb2.md (*thumb2_movhi_insn): Make sure atleast
 one of the operands is a register.

2011-09-19 Jiangning Liu <email address hidden>

 Backport r174803 from mainline
 2011-06-08 Julian Brown <email address hidden>

 * config/arm/arm.c (arm_libcall_uses_aapcs_base): Use correct ABI
 for double-precision helper functions in hard-float mode if only
 single-precision arithmetic is supported in hardware.

Modified:
    branches/ARM/embedded-4_6-branch/gcc/ChangeLog.arm
    branches/ARM/embedded-4_6-branch/gcc/config/arm/arm.c
    branches/ARM/embedded-4_6-branch/gcc/config/arm/thumb2.md
    branches/ARM/embedded-4_6-branch/gcc/fold-const.c

Peter Maydell (pmaydell) wrote :

For the record, this bites QEMU compiled on Natty as well (and results in an immediate crash on startup).

This should be marked FIXED as of 4.7.0 .

Ramana

Changed in gcc:
status: Confirmed → Fix Released
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.