This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ARM gcc generates incorrect code?


Hi,

not sure where the bug is - gcc 4.2.4pre (CVS), binutils 2.17,
cross compiler X86_64 -> ARM BE.

-O2 -fno-strict-aliasing -fno-common -fno-stack-protector -marm
-fno-omit-frame-pointer -mapcs -mno-sched-prolog -mabi=apcs-gnu
-mno-thumb-interwork -march=armv5te -mtune=xscale -mcpu=xscale
-msoft-float -Uarm -fno-omit-frame-pointer -fno-optimize-sibling-calls

Building a test Linux kernel module:

#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/device.h>

#define USE_UACCESS 0

#if USE_UACCESS
#include <asm/uaccess.h>
#else
extern int __get_user_1(void *);

/*
        .global __get_user_1
__get_user_1:
1:      ldrbt   r2, [r0]
        mov     r0, #0
        mov     pc, lr
*/

#define get_user(x,p)							\
	({								\
		register const u8 __user *__p asm("r0") = (p);		\
		register unsigned long __r2 asm("r2");			\
		register int __e asm("r0");				\
		__asm__ __volatile__ (					\
			__asmeq("%0", "r0") __asmeq("%1", "r2")		\
			"bl	__get_user_1"				\
			: "=&r" (__e), "=r" (__r2)			\
			: "0" (__p)					\
			: "lr", "cc");					\
		x = (u8) __r2;						\
		__e;							\
	})

#endif

struct port {
	u8 chan_buf[256];
	unsigned int tx_count;
	u8 modulo;
	struct cdev cdev;
	struct device *dev;
};

static struct class *test_class;
static dev_t rdev;
static struct port *main_port;

static ssize_t test_chan_write(struct file *file, const char __user *buf,
			       size_t count, loff_t *f_pos)
{
	struct port *port = main_port;
	int res = 0;
	unsigned int tail, chan, frame;

	tail = port->tx_count % 2;
	chan = tail % port->modulo;
	frame = tail / port->modulo;

	if (get_user(port->chan_buf[chan * 2 + frame], buf))
		return -EFAULT;
	port->tx_count++;
	res++;
	return res;
}

static const struct file_operations chan_fops = {
	.owner   = THIS_MODULE,
	.llseek  = no_llseek,
	.write   = test_chan_write,
};


static int __init test_init_module(void)
{
	int err;

	if ((err = alloc_chrdev_region(&rdev, 0, 1, "test")))
		return err;

	if (IS_ERR(test_class = class_create(THIS_MODULE, "test"))) {
		printk(KERN_ERR "Can't register device class 'test'\n");
		err = PTR_ERR(test_class);
		goto free_chrdev;
	}

	if (!(main_port = kzalloc(sizeof(*main_port), GFP_KERNEL))) {
		err = -ENOBUFS;
		goto destroy_class;
	}

	main_port->dev = device_create(test_class, NULL, rdev, "test");
	if (IS_ERR(main_port->dev)) {
		err = PTR_ERR(main_port->dev);
		goto free;
	}

	main_port->tx_count = 0;
	main_port->modulo = 2;

	cdev_init(&main_port->cdev, &chan_fops);
	main_port->cdev.owner = THIS_MODULE;
	if ((err = cdev_add(&main_port->cdev, rdev, 1)))
		goto destroy_device;

	dev_set_drvdata(main_port->dev, &main_port);

	printk(KERN_CRIT "start\n");
	return 0;

destroy_device:
	device_unregister(main_port->dev);
free:
	kfree(main_port);
destroy_class:
	class_destroy(test_class);
free_chrdev:
	unregister_chrdev_region(rdev, 1);
	return err;
}

static void __exit test_cleanup_module(void)
{
	printk(KERN_CRIT "tx_count = %u, modulo = %u\n",
	       main_port->tx_count, main_port->modulo);
	cdev_del(&main_port->cdev);
	device_unregister(main_port->dev);
	kfree(main_port);
	class_destroy(test_class);
	unregister_chrdev_region(rdev, 1);
}

MODULE_LICENSE("GPL v2");
module_init(test_init_module);
module_exit(test_cleanup_module);

# Makefile
obj-m	:= ixp-test.o
default:
	make -C /usr/local/build/diskless/xscale_be-router-test-linux SUBDIRS=`pwd` ARCH=arm CROSS_COMPILE=armeb-pc-linux-gnu- modules

gcc produces the following assembly code:

00000000 <test_chan_write>:
   0:   e1a0c00d        mov     ip, sp
   4:   e92dddf0        stmdb   sp!, {r4, r5, r6, r7, r8, sl, fp, ip, lr, pc}
   8:   e24cb004        sub     fp, ip, #4      ; 0x4
   c:   e59f3054        ldr     r3, [pc, #84]   ; 68 <.text+0x68>
  10:   e1a00001        mov     r0, r1		/* buf */
  14:   e5938000        ldr     r8, [r3]
  18:   e598a100        ldr     sl, [r8, #256]	/* port->tx_count */
  1c:   e5d86104        ldrb    r6, [r8, #260]	/* port->modulo */
  20:   e20a4001        and     r4, sl, #1      ; 0x1

  24:   ebfffffe        bl      0 <__get_user_1> /* returns value in R0 */
  28:   e1a01006        mov     r1, r6
  2c:   e1a00004        mov     r0, r4		/* R0 overwritten here */
  30:   e1a07002        mov     r7, r2
  34:   ebfffffe        bl      0 <__umodsi3>
  38:   e1a01006        mov     r1, r6
  3c:   e1a05000        mov     r5, r0
  40:   e1a00004        mov     r0, r4
  44:   ebfffffe        bl      0 <__udivsi3>

/* gcc now tests already overwritten __get_user_1() return value here */
  48:   e3500000        cmp     r0, #0  ; 0x0
  4c:   e0800085        add     r0, r0, r5, lsl #1
  50:   e7c07008        strb    r7, [r0, r8]
  54:   028a3001        addeq   r3, sl, #1      ; 0x1
  58:   13e0000d        mvnne   r0, #13 ; 0xd
  5c:   03a00001        moveq   r0, #1  ; 0x1
  60:   05883100        streq   r3, [r8, #256]
  64:   e89dadf0        ldmia   sp, {r4, r5, r6, r7, r8, sl, fp, sp, pc}
  68:   00000008        andeq   r0, r0, r8

Gcc bug? get_user() bug? Should I file a bug entry?

Gcc-4.1.2 and 4.1-CVS do a similar thing.
-- 
Krzysztof Halasa


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]