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