From: Greg Kroah-Hartman Date: Thu, 24 May 2012 05:05:51 +0000 (-0700) Subject: 3.3-stable patches X-Git-Tag: v3.0.33~40 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=855004fa889d9febfa6c11a51f3e43ac9ea63f9e;p=thirdparty%2Fkernel%2Fstable-queue.git 3.3-stable patches added patches: bio-allocation-failure-due-to-bio_get_nr_vecs.patch block-don-t-mark-buffers-beyond-end-of-disk-as-mapped.patch block-fix-buffer-overflow-when-printing-partition-uuids.patch parisc-fix-crash-in-flush_icache_page_asm-on-pa1.1.patch parisc-fix-pa1.1-oops-on-boot.patch parisc-fix-panic-on-prefetch-null-on-pa7300lc.patch tilegx-enable-syscall_wrappers-support.patch x86-realmode-16-bit-real-mode-code-support-for-relocs-tool.patch --- diff --git a/queue-3.3/bio-allocation-failure-due-to-bio_get_nr_vecs.patch b/queue-3.3/bio-allocation-failure-due-to-bio_get_nr_vecs.patch new file mode 100644 index 00000000000..b171244b354 --- /dev/null +++ b/queue-3.3/bio-allocation-failure-due-to-bio_get_nr_vecs.patch @@ -0,0 +1,47 @@ +From f908ee9463b09ddd05e1c1a0111132212dc05fac Mon Sep 17 00:00:00 2001 +From: Bernd Schubert +Date: Fri, 11 May 2012 16:36:44 +0200 +Subject: bio allocation failure due to bio_get_nr_vecs() + +From: Bernd Schubert + +commit f908ee9463b09ddd05e1c1a0111132212dc05fac upstream. + +The number of bio_get_nr_vecs() is passed down via bio_alloc() to +bvec_alloc_bs(), which fails the bio allocation if +nr_iovecs > BIO_MAX_PAGES. For the underlying caller this causes an +unexpected bio allocation failure. +Limiting to queue_max_segments() is not sufficient, as max_segments +also might be very large. + +bvec_alloc_bs(gfp_mask, nr_iovecs, ) => NULL when nr_iovecs > BIO_MAX_PAGES +bio_alloc_bioset(gfp_mask, nr_iovecs, ...) +bio_alloc(GFP_NOIO, nvecs) +xfs_alloc_ioend_bio() + +Signed-off-by: Bernd Schubert +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman + +--- + fs/bio.c | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/fs/bio.c ++++ b/fs/bio.c +@@ -505,9 +505,14 @@ EXPORT_SYMBOL(bio_clone); + int bio_get_nr_vecs(struct block_device *bdev) + { + struct request_queue *q = bdev_get_queue(bdev); +- return min_t(unsigned, ++ int nr_pages; ++ ++ nr_pages = min_t(unsigned, + queue_max_segments(q), + queue_max_sectors(q) / (PAGE_SIZE >> 9) + 1); ++ ++ return min_t(unsigned, nr_pages, BIO_MAX_PAGES); ++ + } + EXPORT_SYMBOL(bio_get_nr_vecs); + diff --git a/queue-3.3/block-don-t-mark-buffers-beyond-end-of-disk-as-mapped.patch b/queue-3.3/block-don-t-mark-buffers-beyond-end-of-disk-as-mapped.patch new file mode 100644 index 00000000000..2b3b69c6e14 --- /dev/null +++ b/queue-3.3/block-don-t-mark-buffers-beyond-end-of-disk-as-mapped.patch @@ -0,0 +1,150 @@ +From 080399aaaf3531f5b8761ec0ac30ff98891e8686 Mon Sep 17 00:00:00 2001 +From: Jeff Moyer +Date: Fri, 11 May 2012 16:34:10 +0200 +Subject: block: don't mark buffers beyond end of disk as mapped + +From: Jeff Moyer + +commit 080399aaaf3531f5b8761ec0ac30ff98891e8686 upstream. + +Hi, + +We have a bug report open where a squashfs image mounted on ppc64 would +exhibit errors due to trying to read beyond the end of the disk. It can +easily be reproduced by doing the following: + +[root@ibm-p750e-02-lp3 ~]# ls -l install.img +-rw-r--r-- 1 root root 142032896 Apr 30 16:46 install.img +[root@ibm-p750e-02-lp3 ~]# mount -o loop ./install.img /mnt/test +[root@ibm-p750e-02-lp3 ~]# dd if=/dev/loop0 of=/dev/null +dd: reading `/dev/loop0': Input/output error +277376+0 records in +277376+0 records out +142016512 bytes (142 MB) copied, 0.9465 s, 150 MB/s + +In dmesg, you'll find the following: + +squashfs: version 4.0 (2009/01/31) Phillip Lougher +[ 43.106012] attempt to access beyond end of device +[ 43.106029] loop0: rw=0, want=277410, limit=277408 +[ 43.106039] Buffer I/O error on device loop0, logical block 138704 +[ 43.106053] attempt to access beyond end of device +[ 43.106057] loop0: rw=0, want=277412, limit=277408 +[ 43.106061] Buffer I/O error on device loop0, logical block 138705 +[ 43.106066] attempt to access beyond end of device +[ 43.106070] loop0: rw=0, want=277414, limit=277408 +[ 43.106073] Buffer I/O error on device loop0, logical block 138706 +[ 43.106078] attempt to access beyond end of device +[ 43.106081] loop0: rw=0, want=277416, limit=277408 +[ 43.106085] Buffer I/O error on device loop0, logical block 138707 +[ 43.106089] attempt to access beyond end of device +[ 43.106093] loop0: rw=0, want=277418, limit=277408 +[ 43.106096] Buffer I/O error on device loop0, logical block 138708 +[ 43.106101] attempt to access beyond end of device +[ 43.106104] loop0: rw=0, want=277420, limit=277408 +[ 43.106108] Buffer I/O error on device loop0, logical block 138709 +[ 43.106112] attempt to access beyond end of device +[ 43.106116] loop0: rw=0, want=277422, limit=277408 +[ 43.106120] Buffer I/O error on device loop0, logical block 138710 +[ 43.106124] attempt to access beyond end of device +[ 43.106128] loop0: rw=0, want=277424, limit=277408 +[ 43.106131] Buffer I/O error on device loop0, logical block 138711 +[ 43.106135] attempt to access beyond end of device +[ 43.106139] loop0: rw=0, want=277426, limit=277408 +[ 43.106143] Buffer I/O error on device loop0, logical block 138712 +[ 43.106147] attempt to access beyond end of device +[ 43.106151] loop0: rw=0, want=277428, limit=277408 +[ 43.106154] Buffer I/O error on device loop0, logical block 138713 +[ 43.106158] attempt to access beyond end of device +[ 43.106162] loop0: rw=0, want=277430, limit=277408 +[ 43.106166] attempt to access beyond end of device +[ 43.106169] loop0: rw=0, want=277432, limit=277408 +... +[ 43.106307] attempt to access beyond end of device +[ 43.106311] loop0: rw=0, want=277470, limit=2774 + +Squashfs manages to read in the end block(s) of the disk during the +mount operation. Then, when dd reads the block device, it leads to +block_read_full_page being called with buffers that are beyond end of +disk, but are marked as mapped. Thus, it would end up submitting read +I/O against them, resulting in the errors mentioned above. I fixed the +problem by modifying init_page_buffers to only set the buffer mapped if +it fell inside of i_size. + +Cheers, +Jeff + +Signed-off-by: Jeff Moyer +Acked-by: Nick Piggin + +-- + +Changes from v1->v2: re-used max_block, as suggested by Nick Piggin. +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman + +--- + fs/block_dev.c | 6 +++--- + fs/buffer.c | 4 +++- + include/linux/fs.h | 1 + + 3 files changed, 7 insertions(+), 4 deletions(-) + +--- a/fs/block_dev.c ++++ b/fs/block_dev.c +@@ -69,7 +69,7 @@ static void bdev_inode_switch_bdi(struct + spin_unlock(&dst->wb.list_lock); + } + +-static sector_t max_block(struct block_device *bdev) ++sector_t blkdev_max_block(struct block_device *bdev) + { + sector_t retval = ~((sector_t)0); + loff_t sz = i_size_read(bdev->bd_inode); +@@ -162,7 +162,7 @@ static int + blkdev_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh, int create) + { +- if (iblock >= max_block(I_BDEV(inode))) { ++ if (iblock >= blkdev_max_block(I_BDEV(inode))) { + if (create) + return -EIO; + +@@ -184,7 +184,7 @@ static int + blkdev_get_blocks(struct inode *inode, sector_t iblock, + struct buffer_head *bh, int create) + { +- sector_t end_block = max_block(I_BDEV(inode)); ++ sector_t end_block = blkdev_max_block(I_BDEV(inode)); + unsigned long max_blocks = bh->b_size >> inode->i_blkbits; + + if ((iblock + max_blocks) > end_block) { +--- a/fs/buffer.c ++++ b/fs/buffer.c +@@ -921,6 +921,7 @@ init_page_buffers(struct page *page, str + struct buffer_head *head = page_buffers(page); + struct buffer_head *bh = head; + int uptodate = PageUptodate(page); ++ sector_t end_block = blkdev_max_block(I_BDEV(bdev->bd_inode)); + + do { + if (!buffer_mapped(bh)) { +@@ -929,7 +930,8 @@ init_page_buffers(struct page *page, str + bh->b_blocknr = block; + if (uptodate) + set_buffer_uptodate(bh); +- set_buffer_mapped(bh); ++ if (block < end_block) ++ set_buffer_mapped(bh); + } + block++; + bh = bh->b_this_page; +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -2058,6 +2058,7 @@ extern void unregister_blkdev(unsigned i + extern struct block_device *bdget(dev_t); + extern struct block_device *bdgrab(struct block_device *bdev); + extern void bd_set_size(struct block_device *, loff_t size); ++extern sector_t blkdev_max_block(struct block_device *bdev); + extern void bd_forget(struct inode *inode); + extern void bdput(struct block_device *); + extern void invalidate_bdev(struct block_device *); diff --git a/queue-3.3/block-fix-buffer-overflow-when-printing-partition-uuids.patch b/queue-3.3/block-fix-buffer-overflow-when-printing-partition-uuids.patch new file mode 100644 index 00000000000..deb6a3a6710 --- /dev/null +++ b/queue-3.3/block-fix-buffer-overflow-when-printing-partition-uuids.patch @@ -0,0 +1,93 @@ +From 05c69d298c96703741cac9a5cbbf6c53bd55a6e2 Mon Sep 17 00:00:00 2001 +From: Tejun Heo +Date: Tue, 15 May 2012 08:22:04 +0200 +Subject: block: fix buffer overflow when printing partition UUIDs + +From: Tejun Heo + +commit 05c69d298c96703741cac9a5cbbf6c53bd55a6e2 upstream. + +6d1d8050b4bc8 "block, partition: add partition_meta_info to hd_struct" +added part_unpack_uuid() which assumes that the passed in buffer has +enough space for sprintfing "%pU" - 37 characters including '\0'. + +Unfortunately, b5af921ec0233 "init: add support for root devices +specified by partition UUID" supplied 33 bytes buffer to the function +leading to the following panic with stackprotector enabled. + + Kernel panic - not syncing: stack-protector: Kernel stack corrupted in: ffffffff81b14c7e + + [] panic+0xba/0x1c6 + [] ? printk_all_partitions+0x259/0x26xb + [] __stack_chk_fail+0x1b/0x20 + [] printk_all_paritions+0x259/0x26xb + [] mount_block_root+0x1bc/0x27f + [] mount_root+0x57/0x5b + [] prepare_namespace+0x13d/0x176 + [] ? release_tgcred.isra.4+0x330/0x30 + [] kernel_init+0x155/0x15a + [] ? schedule_tail+0x27/0xb0 + [] kernel_thread_helper+0x5/0x10 + [] ? start_kernel+0x3c5/0x3c5 + [] ? gs_change+0x13/0x13 + +Increase the buffer size, remove the dangerous part_unpack_uuid() and +use snprintf() directly from printk_all_partitions(). + +Signed-off-by: Tejun Heo +Reported-by: Szymon Gruszczynski +Cc: Will Drewry +Signed-off-by: Jens Axboe +Signed-off-by: Greg Kroah-Hartman + +--- + block/genhd.c | 10 ++++++---- + include/linux/genhd.h | 6 ------ + 2 files changed, 6 insertions(+), 10 deletions(-) + +--- a/block/genhd.c ++++ b/block/genhd.c +@@ -743,7 +743,7 @@ void __init printk_all_partitions(void) + struct hd_struct *part; + char name_buf[BDEVNAME_SIZE]; + char devt_buf[BDEVT_SIZE]; +- u8 uuid[PARTITION_META_INFO_UUIDLTH * 2 + 1]; ++ char uuid_buf[PARTITION_META_INFO_UUIDLTH * 2 + 5]; + + /* + * Don't show empty devices or things that have been +@@ -762,14 +762,16 @@ void __init printk_all_partitions(void) + while ((part = disk_part_iter_next(&piter))) { + bool is_part0 = part == &disk->part0; + +- uuid[0] = 0; ++ uuid_buf[0] = '\0'; + if (part->info) +- part_unpack_uuid(part->info->uuid, uuid); ++ snprintf(uuid_buf, sizeof(uuid_buf), "%pU", ++ part->info->uuid); + + printk("%s%s %10llu %s %s", is_part0 ? "" : " ", + bdevt_str(part_devt(part), devt_buf), + (unsigned long long)part->nr_sects >> 1, +- disk_name(disk, part->partno, name_buf), uuid); ++ disk_name(disk, part->partno, name_buf), ++ uuid_buf); + if (is_part0) { + if (disk->driverfs_dev != NULL && + disk->driverfs_dev->driver != NULL) +--- a/include/linux/genhd.h ++++ b/include/linux/genhd.h +@@ -222,12 +222,6 @@ static inline void part_pack_uuid(const + } + } + +-static inline char *part_unpack_uuid(const u8 *uuid, char *out) +-{ +- sprintf(out, "%pU", uuid); +- return out; +-} +- + static inline int disk_max_parts(struct gendisk *disk) + { + if (disk->flags & GENHD_FL_EXT_DEVT) diff --git a/queue-3.3/parisc-fix-crash-in-flush_icache_page_asm-on-pa1.1.patch b/queue-3.3/parisc-fix-crash-in-flush_icache_page_asm-on-pa1.1.patch new file mode 100644 index 00000000000..c8383778be2 --- /dev/null +++ b/queue-3.3/parisc-fix-crash-in-flush_icache_page_asm-on-pa1.1.patch @@ -0,0 +1,89 @@ +From 207f583d7179f707f402c36a7bda5ca1fd03ad5b Mon Sep 17 00:00:00 2001 +From: John David Anglin +Date: Wed, 16 May 2012 10:14:52 +0100 +Subject: PARISC: fix crash in flush_icache_page_asm on PA1.1 + +From: John David Anglin + +commit 207f583d7179f707f402c36a7bda5ca1fd03ad5b upstream. + +As pointed out by serveral people, PA1.1 only has a type 26 instruction +meaning that the space register must be explicitly encoded. Not giving an +explicit space means that the compiler uses the type 24 version which is PA2.0 +only resulting in an illegal instruction crash. + +This regression was caused by + + commit f311847c2fcebd81912e2f0caf8a461dec28db41 + Author: James Bottomley + Date: Wed Dec 22 10:22:11 2010 -0600 + + parisc: flush pages through tmpalias space + +Reported-by: Helge Deller +Signed-off-by: John David Anglin +Signed-off-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman + +--- + arch/parisc/kernel/pacache.S | 38 ++++++++++++++++++++------------------ + 1 file changed, 20 insertions(+), 18 deletions(-) + +--- a/arch/parisc/kernel/pacache.S ++++ b/arch/parisc/kernel/pacache.S +@@ -692,7 +692,7 @@ ENTRY(flush_icache_page_asm) + + /* Purge any old translation */ + +- pitlb (%sr0,%r28) ++ pitlb (%sr4,%r28) + + ldil L%icache_stride, %r1 + ldw R%icache_stride(%r1), %r1 +@@ -706,27 +706,29 @@ ENTRY(flush_icache_page_asm) + sub %r25, %r1, %r25 + + +-1: fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) +- fic,m %r1(%r28) ++ /* fic only has the type 26 form on PA1.1, requiring an ++ * explicit space specification, so use %sr4 */ ++1: fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) ++ fic,m %r1(%sr4,%r28) + cmpb,COND(<<) %r28, %r25,1b +- fic,m %r1(%r28) ++ fic,m %r1(%sr4,%r28) + + sync + bv %r0(%r2) +- pitlb (%sr0,%r25) ++ pitlb (%sr4,%r25) + .exit + + .procend diff --git a/queue-3.3/parisc-fix-pa1.1-oops-on-boot.patch b/queue-3.3/parisc-fix-pa1.1-oops-on-boot.patch new file mode 100644 index 00000000000..6ffc82fda93 --- /dev/null +++ b/queue-3.3/parisc-fix-pa1.1-oops-on-boot.patch @@ -0,0 +1,42 @@ +From 5e185581d7c46ddd33cd9c01106d1fc86efb9376 Mon Sep 17 00:00:00 2001 +From: James Bottomley +Date: Tue, 15 May 2012 11:04:19 +0100 +Subject: PARISC: fix PA1.1 oops on boot + +From: James Bottomley + +commit 5e185581d7c46ddd33cd9c01106d1fc86efb9376 upstream. + +All PA1.1 systems have been oopsing on boot since + +commit f311847c2fcebd81912e2f0caf8a461dec28db41 +Author: James Bottomley +Date: Wed Dec 22 10:22:11 2010 -0600 + + parisc: flush pages through tmpalias space + +because a PA2.0 instruction was accidentally introduced into the PA1.1 TLB +insertion interruption path when it was consolidated with the do_alias macro. +Fix the do_alias macro only to use PA2.0 instructions if compiled for 64 bit. + +Signed-off-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman + +--- + arch/parisc/kernel/entry.S | 4 ++++ + 1 file changed, 4 insertions(+) + +--- a/arch/parisc/kernel/entry.S ++++ b/arch/parisc/kernel/entry.S +@@ -581,7 +581,11 @@ + */ + cmpiclr,= 0x01,\tmp,%r0 + ldi (_PAGE_DIRTY|_PAGE_READ|_PAGE_WRITE),\prot ++#ifdef CONFIG_64BIT + depd,z \prot,8,7,\prot ++#else ++ depw,z \prot,8,7,\prot ++#endif + /* + * OK, it is in the temp alias region, check whether "from" or "to". + * Check "subtle" note in pacache.S re: r23/r26. diff --git a/queue-3.3/parisc-fix-panic-on-prefetch-null-on-pa7300lc.patch b/queue-3.3/parisc-fix-panic-on-prefetch-null-on-pa7300lc.patch new file mode 100644 index 00000000000..87b4b6d9b2a --- /dev/null +++ b/queue-3.3/parisc-fix-panic-on-prefetch-null-on-pa7300lc.patch @@ -0,0 +1,37 @@ +From b3cb8674811d1851bbf1486a73d62b90c119b994 Mon Sep 17 00:00:00 2001 +From: James Bottomley +Date: Wed, 16 May 2012 11:10:27 +0100 +Subject: PARISC: fix panic on prefetch(NULL) on PA7300LC + +From: James Bottomley + +commit b3cb8674811d1851bbf1486a73d62b90c119b994 upstream. + +Due to an errata, the PA7300LC generates a TLB miss interruption even on the +prefetch instruction. This means that prefetch(NULL), which is supposed to be +a nop on linux actually generates a NULL deref fault. Fix this by testing the +address of prefetch against NULL before doing the prefetch. + +Signed-off-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman + +--- + arch/parisc/include/asm/prefetch.h | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +--- a/arch/parisc/include/asm/prefetch.h ++++ b/arch/parisc/include/asm/prefetch.h +@@ -21,7 +21,12 @@ + #define ARCH_HAS_PREFETCH + static inline void prefetch(const void *addr) + { +- __asm__("ldw 0(%0), %%r0" : : "r" (addr)); ++ __asm__( ++#ifndef CONFIG_PA20 ++ /* Need to avoid prefetch of NULL on PA7300LC */ ++ " extrw,u,= %0,31,32,%%r0\n" ++#endif ++ " ldw 0(%0), %%r0" : : "r" (addr)); + } + + /* LDD is a PA2.0 addition. */ diff --git a/queue-3.3/series b/queue-3.3/series new file mode 100644 index 00000000000..8900cb45f46 --- /dev/null +++ b/queue-3.3/series @@ -0,0 +1,8 @@ +tilegx-enable-syscall_wrappers-support.patch +bio-allocation-failure-due-to-bio_get_nr_vecs.patch +block-fix-buffer-overflow-when-printing-partition-uuids.patch +block-don-t-mark-buffers-beyond-end-of-disk-as-mapped.patch +parisc-fix-pa1.1-oops-on-boot.patch +parisc-fix-crash-in-flush_icache_page_asm-on-pa1.1.patch +parisc-fix-panic-on-prefetch-null-on-pa7300lc.patch +x86-realmode-16-bit-real-mode-code-support-for-relocs-tool.patch diff --git a/queue-3.3/tilegx-enable-syscall_wrappers-support.patch b/queue-3.3/tilegx-enable-syscall_wrappers-support.patch new file mode 100644 index 00000000000..e9447b3d6e6 --- /dev/null +++ b/queue-3.3/tilegx-enable-syscall_wrappers-support.patch @@ -0,0 +1,37 @@ +From e6d9668e119af44ae5bcd5f1197174531458afe3 Mon Sep 17 00:00:00 2001 +From: Chris Metcalf +Date: Fri, 18 May 2012 13:33:24 -0400 +Subject: tilegx: enable SYSCALL_WRAPPERS support + +From: Chris Metcalf + +commit e6d9668e119af44ae5bcd5f1197174531458afe3 upstream. + +Some discussion with the glibc mailing lists revealed that this was +necessary for 64-bit platforms with MIPS-like sign-extension rules +for 32-bit values. The original symptom was that passing (uid_t)-1 to +setreuid() was failing in programs linked -pthread because of the "setxid" +mechanism for passing setxid-type function arguments to the syscall code. +SYSCALL_WRAPPERS handles ensuring that all syscall arguments end up with +proper sign-extension and is thus the appropriate fix for this problem. + +On other platforms (s390, powerpc, sparc64, and mips) this was fixed +in 2.6.28.6. The general issue is tracked as CVE-2009-0029. + +Signed-off-by: Chris Metcalf +Signed-off-by: Greg Kroah-Hartman + +--- + arch/tile/Kconfig | 1 + + 1 file changed, 1 insertion(+) + +--- a/arch/tile/Kconfig ++++ b/arch/tile/Kconfig +@@ -11,6 +11,7 @@ config TILE + select GENERIC_IRQ_PROBE + select GENERIC_PENDING_IRQ if SMP + select GENERIC_IRQ_SHOW ++ select HAVE_SYSCALL_WRAPPERS if TILEGX + select SYS_HYPERVISOR + select ARCH_HAVE_NMI_SAFE_CMPXCHG if !M386 + diff --git a/queue-3.3/x86-realmode-16-bit-real-mode-code-support-for-relocs-tool.patch b/queue-3.3/x86-realmode-16-bit-real-mode-code-support-for-relocs-tool.patch new file mode 100644 index 00000000000..2155c0b2ca6 --- /dev/null +++ b/queue-3.3/x86-realmode-16-bit-real-mode-code-support-for-relocs-tool.patch @@ -0,0 +1,1629 @@ +From 6520fe5564acf07ade7b18a1272db1184835c487 Mon Sep 17 00:00:00 2001 +From: "H. Peter Anvin" +Date: Tue, 8 May 2012 21:22:24 +0300 +Subject: x86, realmode: 16-bit real-mode code support for relocs tool + +From: "H. Peter Anvin" + +commit 6520fe5564acf07ade7b18a1272db1184835c487 upstream. + +A new option is added to the relocs tool called '--realmode'. +This option causes the generation of 16-bit segment relocations +and 32-bit linear relocations for the real-mode code. When +the real-mode code is moved to the low-memory during kernel +initialization, these relocation entries can be used to +relocate the code properly. + +In the assembly code 16-bit segment relocations must be relative +to the 'real_mode_seg' absolute symbol. Linear relocations must be +relative to a symbol prefixed with 'pa_'. + +16-bit segment relocation is used to load cs:ip in 16-bit code. +Linear relocations are used in the 32-bit code for relocatable +data references. They are declared in the linker script of the +real-mode code. + +The relocs tool is moved to arch/x86/tools/relocs.c, and added new +target archscripts that can be used to build scripts needed building +an architecture. be compiled before building the arch/x86 tree. + +[ hpa: accelerating this because it detects invalid absolute + relocations, a serious bug in binutils 2.22.52.0.x which currently + produces bad kernels. ] + +Signed-off-by: H. Peter Anvin +Link: http://lkml.kernel.org/r/1336501366-28617-2-git-send-email-jarkko.sakkinen@intel.com +Signed-off-by: Jarkko Sakkinen +Signed-off-by: H. Peter Anvin +Signed-off-by: Greg Kroah-Hartman + +diff --git a/Makefile b/Makefile +index 48bd1f5..db2bcf3 100644 +--- a/Makefile ++++ b/Makefile +@@ -442,7 +442,7 @@ asm-generic: + + no-dot-config-targets := clean mrproper distclean \ + cscope gtags TAGS tags help %docs check% coccicheck \ +- include/linux/version.h headers_% archheaders \ ++ include/linux/version.h headers_% archheaders archscripts \ + kernelversion %src-pkg + + config-targets := 0 +@@ -979,7 +979,7 @@ prepare1: prepare2 include/linux/version.h include/generated/utsrelease.h \ + include/config/auto.conf + $(cmd_crmodverdir) + +-archprepare: archheaders prepare1 scripts_basic ++archprepare: archheaders archscripts prepare1 scripts_basic + + prepare0: archprepare FORCE + $(Q)$(MAKE) $(build)=. +@@ -1049,8 +1049,11 @@ hdr-dst = $(if $(KBUILD_HEADERS), dst=include/asm-$(hdr-arch), dst=include/asm) + PHONY += archheaders + archheaders: + ++PHONY += archscripts ++archscripts: ++ + PHONY += __headers +-__headers: include/linux/version.h scripts_basic asm-generic archheaders FORCE ++__headers: include/linux/version.h scripts_basic asm-generic archheaders archscripts FORCE + $(Q)$(MAKE) $(build)=scripts build_unifdef + + PHONY += headers_install_all +diff --git a/arch/x86/Makefile b/arch/x86/Makefile +index 41a7237..94e91e4 100644 +--- a/arch/x86/Makefile ++++ b/arch/x86/Makefile +@@ -134,6 +134,9 @@ KBUILD_CFLAGS += $(call cc-option,-mno-avx,) + KBUILD_CFLAGS += $(mflags-y) + KBUILD_AFLAGS += $(mflags-y) + ++archscripts: ++ $(Q)$(MAKE) $(build)=arch/x86/tools relocs ++ + ### + # Syscall table generation + +diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile +index fd55a2f..e398bb5 100644 +--- a/arch/x86/boot/compressed/Makefile ++++ b/arch/x86/boot/compressed/Makefile +@@ -40,13 +40,12 @@ OBJCOPYFLAGS_vmlinux.bin := -R .comment -S + $(obj)/vmlinux.bin: vmlinux FORCE + $(call if_changed,objcopy) + ++targets += vmlinux.bin.all vmlinux.relocs + +-targets += vmlinux.bin.all vmlinux.relocs relocs +-hostprogs-$(CONFIG_X86_NEED_RELOCS) += relocs +- ++CMD_RELOCS = arch/x86/tools/relocs + quiet_cmd_relocs = RELOCS $@ +- cmd_relocs = $(obj)/relocs $< > $@;$(obj)/relocs --abs-relocs $< +-$(obj)/vmlinux.relocs: vmlinux $(obj)/relocs FORCE ++ cmd_relocs = $(CMD_RELOCS) $< > $@;$(CMD_RELOCS) --abs-relocs $< ++$(obj)/vmlinux.relocs: vmlinux FORCE + $(call if_changed,relocs) + + vmlinux.bin.all-y := $(obj)/vmlinux.bin +diff --git a/arch/x86/boot/compressed/relocs.c b/arch/x86/boot/compressed/relocs.c +deleted file mode 100644 +index fb7117a..0000000 +--- a/arch/x86/boot/compressed/relocs.c ++++ /dev/null +@@ -1,678 +0,0 @@ +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#define USE_BSD +-#include +-#include +-#include +- +-static void die(char *fmt, ...); +- +-#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +-static Elf32_Ehdr ehdr; +-static unsigned long reloc_count, reloc_idx; +-static unsigned long *relocs; +- +-struct section { +- Elf32_Shdr shdr; +- struct section *link; +- Elf32_Sym *symtab; +- Elf32_Rel *reltab; +- char *strtab; +-}; +-static struct section *secs; +- +-/* +- * Following symbols have been audited. There values are constant and do +- * not change if bzImage is loaded at a different physical address than +- * the address for which it has been compiled. Don't warn user about +- * absolute relocations present w.r.t these symbols. +- */ +-static const char abs_sym_regex[] = +- "^(xen_irq_disable_direct_reloc$|" +- "xen_save_fl_direct_reloc$|" +- "VDSO|" +- "__crc_)"; +-static regex_t abs_sym_regex_c; +-static int is_abs_reloc(const char *sym_name) +-{ +- return !regexec(&abs_sym_regex_c, sym_name, 0, NULL, 0); +-} +- +-/* +- * These symbols are known to be relative, even if the linker marks them +- * as absolute (typically defined outside any section in the linker script.) +- */ +-static const char rel_sym_regex[] = +- "^_end$"; +-static regex_t rel_sym_regex_c; +-static int is_rel_reloc(const char *sym_name) +-{ +- return !regexec(&rel_sym_regex_c, sym_name, 0, NULL, 0); +-} +- +-static void regex_init(void) +-{ +- char errbuf[128]; +- int err; +- +- err = regcomp(&abs_sym_regex_c, abs_sym_regex, +- REG_EXTENDED|REG_NOSUB); +- if (err) { +- regerror(err, &abs_sym_regex_c, errbuf, sizeof errbuf); +- die("%s", errbuf); +- } +- +- err = regcomp(&rel_sym_regex_c, rel_sym_regex, +- REG_EXTENDED|REG_NOSUB); +- if (err) { +- regerror(err, &rel_sym_regex_c, errbuf, sizeof errbuf); +- die("%s", errbuf); +- } +-} +- +-static void die(char *fmt, ...) +-{ +- va_list ap; +- va_start(ap, fmt); +- vfprintf(stderr, fmt, ap); +- va_end(ap); +- exit(1); +-} +- +-static const char *sym_type(unsigned type) +-{ +- static const char *type_name[] = { +-#define SYM_TYPE(X) [X] = #X +- SYM_TYPE(STT_NOTYPE), +- SYM_TYPE(STT_OBJECT), +- SYM_TYPE(STT_FUNC), +- SYM_TYPE(STT_SECTION), +- SYM_TYPE(STT_FILE), +- SYM_TYPE(STT_COMMON), +- SYM_TYPE(STT_TLS), +-#undef SYM_TYPE +- }; +- const char *name = "unknown sym type name"; +- if (type < ARRAY_SIZE(type_name)) { +- name = type_name[type]; +- } +- return name; +-} +- +-static const char *sym_bind(unsigned bind) +-{ +- static const char *bind_name[] = { +-#define SYM_BIND(X) [X] = #X +- SYM_BIND(STB_LOCAL), +- SYM_BIND(STB_GLOBAL), +- SYM_BIND(STB_WEAK), +-#undef SYM_BIND +- }; +- const char *name = "unknown sym bind name"; +- if (bind < ARRAY_SIZE(bind_name)) { +- name = bind_name[bind]; +- } +- return name; +-} +- +-static const char *sym_visibility(unsigned visibility) +-{ +- static const char *visibility_name[] = { +-#define SYM_VISIBILITY(X) [X] = #X +- SYM_VISIBILITY(STV_DEFAULT), +- SYM_VISIBILITY(STV_INTERNAL), +- SYM_VISIBILITY(STV_HIDDEN), +- SYM_VISIBILITY(STV_PROTECTED), +-#undef SYM_VISIBILITY +- }; +- const char *name = "unknown sym visibility name"; +- if (visibility < ARRAY_SIZE(visibility_name)) { +- name = visibility_name[visibility]; +- } +- return name; +-} +- +-static const char *rel_type(unsigned type) +-{ +- static const char *type_name[] = { +-#define REL_TYPE(X) [X] = #X +- REL_TYPE(R_386_NONE), +- REL_TYPE(R_386_32), +- REL_TYPE(R_386_PC32), +- REL_TYPE(R_386_GOT32), +- REL_TYPE(R_386_PLT32), +- REL_TYPE(R_386_COPY), +- REL_TYPE(R_386_GLOB_DAT), +- REL_TYPE(R_386_JMP_SLOT), +- REL_TYPE(R_386_RELATIVE), +- REL_TYPE(R_386_GOTOFF), +- REL_TYPE(R_386_GOTPC), +-#undef REL_TYPE +- }; +- const char *name = "unknown type rel type name"; +- if (type < ARRAY_SIZE(type_name) && type_name[type]) { +- name = type_name[type]; +- } +- return name; +-} +- +-static const char *sec_name(unsigned shndx) +-{ +- const char *sec_strtab; +- const char *name; +- sec_strtab = secs[ehdr.e_shstrndx].strtab; +- name = ""; +- if (shndx < ehdr.e_shnum) { +- name = sec_strtab + secs[shndx].shdr.sh_name; +- } +- else if (shndx == SHN_ABS) { +- name = "ABSOLUTE"; +- } +- else if (shndx == SHN_COMMON) { +- name = "COMMON"; +- } +- return name; +-} +- +-static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym) +-{ +- const char *name; +- name = ""; +- if (sym->st_name) { +- name = sym_strtab + sym->st_name; +- } +- else { +- name = sec_name(secs[sym->st_shndx].shdr.sh_name); +- } +- return name; +-} +- +- +- +-#if BYTE_ORDER == LITTLE_ENDIAN +-#define le16_to_cpu(val) (val) +-#define le32_to_cpu(val) (val) +-#endif +-#if BYTE_ORDER == BIG_ENDIAN +-#define le16_to_cpu(val) bswap_16(val) +-#define le32_to_cpu(val) bswap_32(val) +-#endif +- +-static uint16_t elf16_to_cpu(uint16_t val) +-{ +- return le16_to_cpu(val); +-} +- +-static uint32_t elf32_to_cpu(uint32_t val) +-{ +- return le32_to_cpu(val); +-} +- +-static void read_ehdr(FILE *fp) +-{ +- if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { +- die("Cannot read ELF header: %s\n", +- strerror(errno)); +- } +- if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { +- die("No ELF magic\n"); +- } +- if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) { +- die("Not a 32 bit executable\n"); +- } +- if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { +- die("Not a LSB ELF executable\n"); +- } +- if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { +- die("Unknown ELF version\n"); +- } +- /* Convert the fields to native endian */ +- ehdr.e_type = elf16_to_cpu(ehdr.e_type); +- ehdr.e_machine = elf16_to_cpu(ehdr.e_machine); +- ehdr.e_version = elf32_to_cpu(ehdr.e_version); +- ehdr.e_entry = elf32_to_cpu(ehdr.e_entry); +- ehdr.e_phoff = elf32_to_cpu(ehdr.e_phoff); +- ehdr.e_shoff = elf32_to_cpu(ehdr.e_shoff); +- ehdr.e_flags = elf32_to_cpu(ehdr.e_flags); +- ehdr.e_ehsize = elf16_to_cpu(ehdr.e_ehsize); +- ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize); +- ehdr.e_phnum = elf16_to_cpu(ehdr.e_phnum); +- ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize); +- ehdr.e_shnum = elf16_to_cpu(ehdr.e_shnum); +- ehdr.e_shstrndx = elf16_to_cpu(ehdr.e_shstrndx); +- +- if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) { +- die("Unsupported ELF header type\n"); +- } +- if (ehdr.e_machine != EM_386) { +- die("Not for x86\n"); +- } +- if (ehdr.e_version != EV_CURRENT) { +- die("Unknown ELF version\n"); +- } +- if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) { +- die("Bad Elf header size\n"); +- } +- if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { +- die("Bad program header entry\n"); +- } +- if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) { +- die("Bad section header entry\n"); +- } +- if (ehdr.e_shstrndx >= ehdr.e_shnum) { +- die("String table index out of bounds\n"); +- } +-} +- +-static void read_shdrs(FILE *fp) +-{ +- int i; +- Elf32_Shdr shdr; +- +- secs = calloc(ehdr.e_shnum, sizeof(struct section)); +- if (!secs) { +- die("Unable to allocate %d section headers\n", +- ehdr.e_shnum); +- } +- if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) { +- die("Seek to %d failed: %s\n", +- ehdr.e_shoff, strerror(errno)); +- } +- for (i = 0; i < ehdr.e_shnum; i++) { +- struct section *sec = &secs[i]; +- if (fread(&shdr, sizeof shdr, 1, fp) != 1) +- die("Cannot read ELF section headers %d/%d: %s\n", +- i, ehdr.e_shnum, strerror(errno)); +- sec->shdr.sh_name = elf32_to_cpu(shdr.sh_name); +- sec->shdr.sh_type = elf32_to_cpu(shdr.sh_type); +- sec->shdr.sh_flags = elf32_to_cpu(shdr.sh_flags); +- sec->shdr.sh_addr = elf32_to_cpu(shdr.sh_addr); +- sec->shdr.sh_offset = elf32_to_cpu(shdr.sh_offset); +- sec->shdr.sh_size = elf32_to_cpu(shdr.sh_size); +- sec->shdr.sh_link = elf32_to_cpu(shdr.sh_link); +- sec->shdr.sh_info = elf32_to_cpu(shdr.sh_info); +- sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign); +- sec->shdr.sh_entsize = elf32_to_cpu(shdr.sh_entsize); +- if (sec->shdr.sh_link < ehdr.e_shnum) +- sec->link = &secs[sec->shdr.sh_link]; +- } +- +-} +- +-static void read_strtabs(FILE *fp) +-{ +- int i; +- for (i = 0; i < ehdr.e_shnum; i++) { +- struct section *sec = &secs[i]; +- if (sec->shdr.sh_type != SHT_STRTAB) { +- continue; +- } +- sec->strtab = malloc(sec->shdr.sh_size); +- if (!sec->strtab) { +- die("malloc of %d bytes for strtab failed\n", +- sec->shdr.sh_size); +- } +- if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { +- die("Seek to %d failed: %s\n", +- sec->shdr.sh_offset, strerror(errno)); +- } +- if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) +- != sec->shdr.sh_size) { +- die("Cannot read symbol table: %s\n", +- strerror(errno)); +- } +- } +-} +- +-static void read_symtabs(FILE *fp) +-{ +- int i,j; +- for (i = 0; i < ehdr.e_shnum; i++) { +- struct section *sec = &secs[i]; +- if (sec->shdr.sh_type != SHT_SYMTAB) { +- continue; +- } +- sec->symtab = malloc(sec->shdr.sh_size); +- if (!sec->symtab) { +- die("malloc of %d bytes for symtab failed\n", +- sec->shdr.sh_size); +- } +- if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { +- die("Seek to %d failed: %s\n", +- sec->shdr.sh_offset, strerror(errno)); +- } +- if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) +- != sec->shdr.sh_size) { +- die("Cannot read symbol table: %s\n", +- strerror(errno)); +- } +- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { +- Elf32_Sym *sym = &sec->symtab[j]; +- sym->st_name = elf32_to_cpu(sym->st_name); +- sym->st_value = elf32_to_cpu(sym->st_value); +- sym->st_size = elf32_to_cpu(sym->st_size); +- sym->st_shndx = elf16_to_cpu(sym->st_shndx); +- } +- } +-} +- +- +-static void read_relocs(FILE *fp) +-{ +- int i,j; +- for (i = 0; i < ehdr.e_shnum; i++) { +- struct section *sec = &secs[i]; +- if (sec->shdr.sh_type != SHT_REL) { +- continue; +- } +- sec->reltab = malloc(sec->shdr.sh_size); +- if (!sec->reltab) { +- die("malloc of %d bytes for relocs failed\n", +- sec->shdr.sh_size); +- } +- if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { +- die("Seek to %d failed: %s\n", +- sec->shdr.sh_offset, strerror(errno)); +- } +- if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) +- != sec->shdr.sh_size) { +- die("Cannot read symbol table: %s\n", +- strerror(errno)); +- } +- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { +- Elf32_Rel *rel = &sec->reltab[j]; +- rel->r_offset = elf32_to_cpu(rel->r_offset); +- rel->r_info = elf32_to_cpu(rel->r_info); +- } +- } +-} +- +- +-static void print_absolute_symbols(void) +-{ +- int i; +- printf("Absolute symbols\n"); +- printf(" Num: Value Size Type Bind Visibility Name\n"); +- for (i = 0; i < ehdr.e_shnum; i++) { +- struct section *sec = &secs[i]; +- char *sym_strtab; +- int j; +- +- if (sec->shdr.sh_type != SHT_SYMTAB) { +- continue; +- } +- sym_strtab = sec->link->strtab; +- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { +- Elf32_Sym *sym; +- const char *name; +- sym = &sec->symtab[j]; +- name = sym_name(sym_strtab, sym); +- if (sym->st_shndx != SHN_ABS) { +- continue; +- } +- printf("%5d %08x %5d %10s %10s %12s %s\n", +- j, sym->st_value, sym->st_size, +- sym_type(ELF32_ST_TYPE(sym->st_info)), +- sym_bind(ELF32_ST_BIND(sym->st_info)), +- sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)), +- name); +- } +- } +- printf("\n"); +-} +- +-static void print_absolute_relocs(void) +-{ +- int i, printed = 0; +- +- for (i = 0; i < ehdr.e_shnum; i++) { +- struct section *sec = &secs[i]; +- struct section *sec_applies, *sec_symtab; +- char *sym_strtab; +- Elf32_Sym *sh_symtab; +- int j; +- if (sec->shdr.sh_type != SHT_REL) { +- continue; +- } +- sec_symtab = sec->link; +- sec_applies = &secs[sec->shdr.sh_info]; +- if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { +- continue; +- } +- sh_symtab = sec_symtab->symtab; +- sym_strtab = sec_symtab->link->strtab; +- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { +- Elf32_Rel *rel; +- Elf32_Sym *sym; +- const char *name; +- rel = &sec->reltab[j]; +- sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; +- name = sym_name(sym_strtab, sym); +- if (sym->st_shndx != SHN_ABS) { +- continue; +- } +- +- /* Absolute symbols are not relocated if bzImage is +- * loaded at a non-compiled address. Display a warning +- * to user at compile time about the absolute +- * relocations present. +- * +- * User need to audit the code to make sure +- * some symbols which should have been section +- * relative have not become absolute because of some +- * linker optimization or wrong programming usage. +- * +- * Before warning check if this absolute symbol +- * relocation is harmless. +- */ +- if (is_abs_reloc(name) || is_rel_reloc(name)) +- continue; +- +- if (!printed) { +- printf("WARNING: Absolute relocations" +- " present\n"); +- printf("Offset Info Type Sym.Value " +- "Sym.Name\n"); +- printed = 1; +- } +- +- printf("%08x %08x %10s %08x %s\n", +- rel->r_offset, +- rel->r_info, +- rel_type(ELF32_R_TYPE(rel->r_info)), +- sym->st_value, +- name); +- } +- } +- +- if (printed) +- printf("\n"); +-} +- +-static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym)) +-{ +- int i; +- /* Walk through the relocations */ +- for (i = 0; i < ehdr.e_shnum; i++) { +- char *sym_strtab; +- Elf32_Sym *sh_symtab; +- struct section *sec_applies, *sec_symtab; +- int j; +- struct section *sec = &secs[i]; +- +- if (sec->shdr.sh_type != SHT_REL) { +- continue; +- } +- sec_symtab = sec->link; +- sec_applies = &secs[sec->shdr.sh_info]; +- if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { +- continue; +- } +- sh_symtab = sec_symtab->symtab; +- sym_strtab = sec_symtab->link->strtab; +- for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { +- Elf32_Rel *rel; +- Elf32_Sym *sym; +- unsigned r_type; +- rel = &sec->reltab[j]; +- sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; +- r_type = ELF32_R_TYPE(rel->r_info); +- /* Don't visit relocations to absolute symbols */ +- if (sym->st_shndx == SHN_ABS && +- !is_rel_reloc(sym_name(sym_strtab, sym))) { +- continue; +- } +- switch (r_type) { +- case R_386_NONE: +- case R_386_PC32: +- /* +- * NONE can be ignored and and PC relative +- * relocations don't need to be adjusted. +- */ +- break; +- case R_386_32: +- /* Visit relocations that need to be adjusted */ +- visit(rel, sym); +- break; +- default: +- die("Unsupported relocation type: %s (%d)\n", +- rel_type(r_type), r_type); +- break; +- } +- } +- } +-} +- +-static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym) +-{ +- reloc_count += 1; +-} +- +-static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) +-{ +- /* Remember the address that needs to be adjusted. */ +- relocs[reloc_idx++] = rel->r_offset; +-} +- +-static int cmp_relocs(const void *va, const void *vb) +-{ +- const unsigned long *a, *b; +- a = va; b = vb; +- return (*a == *b)? 0 : (*a > *b)? 1 : -1; +-} +- +-static void emit_relocs(int as_text) +-{ +- int i; +- /* Count how many relocations I have and allocate space for them. */ +- reloc_count = 0; +- walk_relocs(count_reloc); +- relocs = malloc(reloc_count * sizeof(relocs[0])); +- if (!relocs) { +- die("malloc of %d entries for relocs failed\n", +- reloc_count); +- } +- /* Collect up the relocations */ +- reloc_idx = 0; +- walk_relocs(collect_reloc); +- +- /* Order the relocations for more efficient processing */ +- qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); +- +- /* Print the relocations */ +- if (as_text) { +- /* Print the relocations in a form suitable that +- * gas will like. +- */ +- printf(".section \".data.reloc\",\"a\"\n"); +- printf(".balign 4\n"); +- for (i = 0; i < reloc_count; i++) { +- printf("\t .long 0x%08lx\n", relocs[i]); +- } +- printf("\n"); +- } +- else { +- unsigned char buf[4]; +- /* Print a stop */ +- fwrite("\0\0\0\0", 4, 1, stdout); +- /* Now print each relocation */ +- for (i = 0; i < reloc_count; i++) { +- put_unaligned_le32(relocs[i], buf); +- fwrite(buf, 4, 1, stdout); +- } +- } +-} +- +-static void usage(void) +-{ +- die("relocs [--abs-syms |--abs-relocs | --text] vmlinux\n"); +-} +- +-int main(int argc, char **argv) +-{ +- int show_absolute_syms, show_absolute_relocs; +- int as_text; +- const char *fname; +- FILE *fp; +- int i; +- +- regex_init(); +- +- show_absolute_syms = 0; +- show_absolute_relocs = 0; +- as_text = 0; +- fname = NULL; +- for (i = 1; i < argc; i++) { +- char *arg = argv[i]; +- if (*arg == '-') { +- if (strcmp(argv[1], "--abs-syms") == 0) { +- show_absolute_syms = 1; +- continue; +- } +- +- if (strcmp(argv[1], "--abs-relocs") == 0) { +- show_absolute_relocs = 1; +- continue; +- } +- else if (strcmp(argv[1], "--text") == 0) { +- as_text = 1; +- continue; +- } +- } +- else if (!fname) { +- fname = arg; +- continue; +- } +- usage(); +- } +- if (!fname) { +- usage(); +- } +- fp = fopen(fname, "r"); +- if (!fp) { +- die("Cannot open %s: %s\n", +- fname, strerror(errno)); +- } +- read_ehdr(fp); +- read_shdrs(fp); +- read_strtabs(fp); +- read_symtabs(fp); +- read_relocs(fp); +- if (show_absolute_syms) { +- print_absolute_symbols(); +- return 0; +- } +- if (show_absolute_relocs) { +- print_absolute_relocs(); +- return 0; +- } +- emit_relocs(as_text); +- return 0; +-} +diff --git a/arch/x86/tools/.gitignore b/arch/x86/tools/.gitignore +new file mode 100644 +index 0000000..be0ed06 +--- /dev/null ++++ b/arch/x86/tools/.gitignore +@@ -0,0 +1 @@ ++relocs +diff --git a/arch/x86/tools/Makefile b/arch/x86/tools/Makefile +index d511aa9..733057b 100644 +--- a/arch/x86/tools/Makefile ++++ b/arch/x86/tools/Makefile +@@ -36,3 +36,7 @@ HOSTCFLAGS_insn_sanity.o := -Wall -I$(objtree)/arch/x86/lib/ -I$(srctree)/arch/x + $(obj)/test_get_len.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c + + $(obj)/insn_sanity.o: $(srctree)/arch/x86/lib/insn.c $(srctree)/arch/x86/lib/inat.c $(srctree)/arch/x86/include/asm/inat_types.h $(srctree)/arch/x86/include/asm/inat.h $(srctree)/arch/x86/include/asm/insn.h $(objtree)/arch/x86/lib/inat-tables.c ++ ++HOST_EXTRACFLAGS += -I$(srctree)/tools/include ++hostprogs-y += relocs ++relocs: $(obj)/relocs +diff --git a/arch/x86/tools/relocs.c b/arch/x86/tools/relocs.c +new file mode 100644 +index 0000000..0291470 +--- /dev/null ++++ b/arch/x86/tools/relocs.c +@@ -0,0 +1,797 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#define USE_BSD ++#include ++#include ++#include ++ ++static void die(char *fmt, ...); ++ ++#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) ++static Elf32_Ehdr ehdr; ++static unsigned long reloc_count, reloc_idx; ++static unsigned long *relocs; ++static unsigned long reloc16_count, reloc16_idx; ++static unsigned long *relocs16; ++ ++struct section { ++ Elf32_Shdr shdr; ++ struct section *link; ++ Elf32_Sym *symtab; ++ Elf32_Rel *reltab; ++ char *strtab; ++}; ++static struct section *secs; ++ ++enum symtype { ++ S_ABS, ++ S_REL, ++ S_SEG, ++ S_LIN, ++ S_NSYMTYPES ++}; ++ ++static const char * const sym_regex_kernel[S_NSYMTYPES] = { ++/* ++ * Following symbols have been audited. There values are constant and do ++ * not change if bzImage is loaded at a different physical address than ++ * the address for which it has been compiled. Don't warn user about ++ * absolute relocations present w.r.t these symbols. ++ */ ++ [S_ABS] = ++ "^(xen_irq_disable_direct_reloc$|" ++ "xen_save_fl_direct_reloc$|" ++ "VDSO|" ++ "__crc_)", ++ ++/* ++ * These symbols are known to be relative, even if the linker marks them ++ * as absolute (typically defined outside any section in the linker script.) ++ */ ++ [S_REL] = ++ "^_end$", ++}; ++ ++ ++static const char * const sym_regex_realmode[S_NSYMTYPES] = { ++/* ++ * These are 16-bit segment symbols when compiling 16-bit code. ++ */ ++ [S_SEG] = ++ "^real_mode_seg$", ++ ++/* ++ * These are offsets belonging to segments, as opposed to linear addresses, ++ * when compiling 16-bit code. ++ */ ++ [S_LIN] = ++ "^pa_", ++}; ++ ++static const char * const *sym_regex; ++ ++static regex_t sym_regex_c[S_NSYMTYPES]; ++static int is_reloc(enum symtype type, const char *sym_name) ++{ ++ return sym_regex[type] && ++ !regexec(&sym_regex_c[type], sym_name, 0, NULL, 0); ++} ++ ++static void regex_init(int use_real_mode) ++{ ++ char errbuf[128]; ++ int err; ++ int i; ++ ++ if (use_real_mode) ++ sym_regex = sym_regex_realmode; ++ else ++ sym_regex = sym_regex_kernel; ++ ++ for (i = 0; i < S_NSYMTYPES; i++) { ++ if (!sym_regex[i]) ++ continue; ++ ++ err = regcomp(&sym_regex_c[i], sym_regex[i], ++ REG_EXTENDED|REG_NOSUB); ++ ++ if (err) { ++ regerror(err, &sym_regex_c[i], errbuf, sizeof errbuf); ++ die("%s", errbuf); ++ } ++ } ++} ++ ++static void die(char *fmt, ...) ++{ ++ va_list ap; ++ va_start(ap, fmt); ++ vfprintf(stderr, fmt, ap); ++ va_end(ap); ++ exit(1); ++} ++ ++static const char *sym_type(unsigned type) ++{ ++ static const char *type_name[] = { ++#define SYM_TYPE(X) [X] = #X ++ SYM_TYPE(STT_NOTYPE), ++ SYM_TYPE(STT_OBJECT), ++ SYM_TYPE(STT_FUNC), ++ SYM_TYPE(STT_SECTION), ++ SYM_TYPE(STT_FILE), ++ SYM_TYPE(STT_COMMON), ++ SYM_TYPE(STT_TLS), ++#undef SYM_TYPE ++ }; ++ const char *name = "unknown sym type name"; ++ if (type < ARRAY_SIZE(type_name)) { ++ name = type_name[type]; ++ } ++ return name; ++} ++ ++static const char *sym_bind(unsigned bind) ++{ ++ static const char *bind_name[] = { ++#define SYM_BIND(X) [X] = #X ++ SYM_BIND(STB_LOCAL), ++ SYM_BIND(STB_GLOBAL), ++ SYM_BIND(STB_WEAK), ++#undef SYM_BIND ++ }; ++ const char *name = "unknown sym bind name"; ++ if (bind < ARRAY_SIZE(bind_name)) { ++ name = bind_name[bind]; ++ } ++ return name; ++} ++ ++static const char *sym_visibility(unsigned visibility) ++{ ++ static const char *visibility_name[] = { ++#define SYM_VISIBILITY(X) [X] = #X ++ SYM_VISIBILITY(STV_DEFAULT), ++ SYM_VISIBILITY(STV_INTERNAL), ++ SYM_VISIBILITY(STV_HIDDEN), ++ SYM_VISIBILITY(STV_PROTECTED), ++#undef SYM_VISIBILITY ++ }; ++ const char *name = "unknown sym visibility name"; ++ if (visibility < ARRAY_SIZE(visibility_name)) { ++ name = visibility_name[visibility]; ++ } ++ return name; ++} ++ ++static const char *rel_type(unsigned type) ++{ ++ static const char *type_name[] = { ++#define REL_TYPE(X) [X] = #X ++ REL_TYPE(R_386_NONE), ++ REL_TYPE(R_386_32), ++ REL_TYPE(R_386_PC32), ++ REL_TYPE(R_386_GOT32), ++ REL_TYPE(R_386_PLT32), ++ REL_TYPE(R_386_COPY), ++ REL_TYPE(R_386_GLOB_DAT), ++ REL_TYPE(R_386_JMP_SLOT), ++ REL_TYPE(R_386_RELATIVE), ++ REL_TYPE(R_386_GOTOFF), ++ REL_TYPE(R_386_GOTPC), ++ REL_TYPE(R_386_8), ++ REL_TYPE(R_386_PC8), ++ REL_TYPE(R_386_16), ++ REL_TYPE(R_386_PC16), ++#undef REL_TYPE ++ }; ++ const char *name = "unknown type rel type name"; ++ if (type < ARRAY_SIZE(type_name) && type_name[type]) { ++ name = type_name[type]; ++ } ++ return name; ++} ++ ++static const char *sec_name(unsigned shndx) ++{ ++ const char *sec_strtab; ++ const char *name; ++ sec_strtab = secs[ehdr.e_shstrndx].strtab; ++ name = ""; ++ if (shndx < ehdr.e_shnum) { ++ name = sec_strtab + secs[shndx].shdr.sh_name; ++ } ++ else if (shndx == SHN_ABS) { ++ name = "ABSOLUTE"; ++ } ++ else if (shndx == SHN_COMMON) { ++ name = "COMMON"; ++ } ++ return name; ++} ++ ++static const char *sym_name(const char *sym_strtab, Elf32_Sym *sym) ++{ ++ const char *name; ++ name = ""; ++ if (sym->st_name) { ++ name = sym_strtab + sym->st_name; ++ } ++ else { ++ name = sec_name(sym->st_shndx); ++ } ++ return name; ++} ++ ++ ++ ++#if BYTE_ORDER == LITTLE_ENDIAN ++#define le16_to_cpu(val) (val) ++#define le32_to_cpu(val) (val) ++#endif ++#if BYTE_ORDER == BIG_ENDIAN ++#define le16_to_cpu(val) bswap_16(val) ++#define le32_to_cpu(val) bswap_32(val) ++#endif ++ ++static uint16_t elf16_to_cpu(uint16_t val) ++{ ++ return le16_to_cpu(val); ++} ++ ++static uint32_t elf32_to_cpu(uint32_t val) ++{ ++ return le32_to_cpu(val); ++} ++ ++static void read_ehdr(FILE *fp) ++{ ++ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1) { ++ die("Cannot read ELF header: %s\n", ++ strerror(errno)); ++ } ++ if (memcmp(ehdr.e_ident, ELFMAG, SELFMAG) != 0) { ++ die("No ELF magic\n"); ++ } ++ if (ehdr.e_ident[EI_CLASS] != ELFCLASS32) { ++ die("Not a 32 bit executable\n"); ++ } ++ if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) { ++ die("Not a LSB ELF executable\n"); ++ } ++ if (ehdr.e_ident[EI_VERSION] != EV_CURRENT) { ++ die("Unknown ELF version\n"); ++ } ++ /* Convert the fields to native endian */ ++ ehdr.e_type = elf16_to_cpu(ehdr.e_type); ++ ehdr.e_machine = elf16_to_cpu(ehdr.e_machine); ++ ehdr.e_version = elf32_to_cpu(ehdr.e_version); ++ ehdr.e_entry = elf32_to_cpu(ehdr.e_entry); ++ ehdr.e_phoff = elf32_to_cpu(ehdr.e_phoff); ++ ehdr.e_shoff = elf32_to_cpu(ehdr.e_shoff); ++ ehdr.e_flags = elf32_to_cpu(ehdr.e_flags); ++ ehdr.e_ehsize = elf16_to_cpu(ehdr.e_ehsize); ++ ehdr.e_phentsize = elf16_to_cpu(ehdr.e_phentsize); ++ ehdr.e_phnum = elf16_to_cpu(ehdr.e_phnum); ++ ehdr.e_shentsize = elf16_to_cpu(ehdr.e_shentsize); ++ ehdr.e_shnum = elf16_to_cpu(ehdr.e_shnum); ++ ehdr.e_shstrndx = elf16_to_cpu(ehdr.e_shstrndx); ++ ++ if ((ehdr.e_type != ET_EXEC) && (ehdr.e_type != ET_DYN)) { ++ die("Unsupported ELF header type\n"); ++ } ++ if (ehdr.e_machine != EM_386) { ++ die("Not for x86\n"); ++ } ++ if (ehdr.e_version != EV_CURRENT) { ++ die("Unknown ELF version\n"); ++ } ++ if (ehdr.e_ehsize != sizeof(Elf32_Ehdr)) { ++ die("Bad Elf header size\n"); ++ } ++ if (ehdr.e_phentsize != sizeof(Elf32_Phdr)) { ++ die("Bad program header entry\n"); ++ } ++ if (ehdr.e_shentsize != sizeof(Elf32_Shdr)) { ++ die("Bad section header entry\n"); ++ } ++ if (ehdr.e_shstrndx >= ehdr.e_shnum) { ++ die("String table index out of bounds\n"); ++ } ++} ++ ++static void read_shdrs(FILE *fp) ++{ ++ int i; ++ Elf32_Shdr shdr; ++ ++ secs = calloc(ehdr.e_shnum, sizeof(struct section)); ++ if (!secs) { ++ die("Unable to allocate %d section headers\n", ++ ehdr.e_shnum); ++ } ++ if (fseek(fp, ehdr.e_shoff, SEEK_SET) < 0) { ++ die("Seek to %d failed: %s\n", ++ ehdr.e_shoff, strerror(errno)); ++ } ++ for (i = 0; i < ehdr.e_shnum; i++) { ++ struct section *sec = &secs[i]; ++ if (fread(&shdr, sizeof shdr, 1, fp) != 1) ++ die("Cannot read ELF section headers %d/%d: %s\n", ++ i, ehdr.e_shnum, strerror(errno)); ++ sec->shdr.sh_name = elf32_to_cpu(shdr.sh_name); ++ sec->shdr.sh_type = elf32_to_cpu(shdr.sh_type); ++ sec->shdr.sh_flags = elf32_to_cpu(shdr.sh_flags); ++ sec->shdr.sh_addr = elf32_to_cpu(shdr.sh_addr); ++ sec->shdr.sh_offset = elf32_to_cpu(shdr.sh_offset); ++ sec->shdr.sh_size = elf32_to_cpu(shdr.sh_size); ++ sec->shdr.sh_link = elf32_to_cpu(shdr.sh_link); ++ sec->shdr.sh_info = elf32_to_cpu(shdr.sh_info); ++ sec->shdr.sh_addralign = elf32_to_cpu(shdr.sh_addralign); ++ sec->shdr.sh_entsize = elf32_to_cpu(shdr.sh_entsize); ++ if (sec->shdr.sh_link < ehdr.e_shnum) ++ sec->link = &secs[sec->shdr.sh_link]; ++ } ++ ++} ++ ++static void read_strtabs(FILE *fp) ++{ ++ int i; ++ for (i = 0; i < ehdr.e_shnum; i++) { ++ struct section *sec = &secs[i]; ++ if (sec->shdr.sh_type != SHT_STRTAB) { ++ continue; ++ } ++ sec->strtab = malloc(sec->shdr.sh_size); ++ if (!sec->strtab) { ++ die("malloc of %d bytes for strtab failed\n", ++ sec->shdr.sh_size); ++ } ++ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { ++ die("Seek to %d failed: %s\n", ++ sec->shdr.sh_offset, strerror(errno)); ++ } ++ if (fread(sec->strtab, 1, sec->shdr.sh_size, fp) ++ != sec->shdr.sh_size) { ++ die("Cannot read symbol table: %s\n", ++ strerror(errno)); ++ } ++ } ++} ++ ++static void read_symtabs(FILE *fp) ++{ ++ int i,j; ++ for (i = 0; i < ehdr.e_shnum; i++) { ++ struct section *sec = &secs[i]; ++ if (sec->shdr.sh_type != SHT_SYMTAB) { ++ continue; ++ } ++ sec->symtab = malloc(sec->shdr.sh_size); ++ if (!sec->symtab) { ++ die("malloc of %d bytes for symtab failed\n", ++ sec->shdr.sh_size); ++ } ++ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { ++ die("Seek to %d failed: %s\n", ++ sec->shdr.sh_offset, strerror(errno)); ++ } ++ if (fread(sec->symtab, 1, sec->shdr.sh_size, fp) ++ != sec->shdr.sh_size) { ++ die("Cannot read symbol table: %s\n", ++ strerror(errno)); ++ } ++ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { ++ Elf32_Sym *sym = &sec->symtab[j]; ++ sym->st_name = elf32_to_cpu(sym->st_name); ++ sym->st_value = elf32_to_cpu(sym->st_value); ++ sym->st_size = elf32_to_cpu(sym->st_size); ++ sym->st_shndx = elf16_to_cpu(sym->st_shndx); ++ } ++ } ++} ++ ++ ++static void read_relocs(FILE *fp) ++{ ++ int i,j; ++ for (i = 0; i < ehdr.e_shnum; i++) { ++ struct section *sec = &secs[i]; ++ if (sec->shdr.sh_type != SHT_REL) { ++ continue; ++ } ++ sec->reltab = malloc(sec->shdr.sh_size); ++ if (!sec->reltab) { ++ die("malloc of %d bytes for relocs failed\n", ++ sec->shdr.sh_size); ++ } ++ if (fseek(fp, sec->shdr.sh_offset, SEEK_SET) < 0) { ++ die("Seek to %d failed: %s\n", ++ sec->shdr.sh_offset, strerror(errno)); ++ } ++ if (fread(sec->reltab, 1, sec->shdr.sh_size, fp) ++ != sec->shdr.sh_size) { ++ die("Cannot read symbol table: %s\n", ++ strerror(errno)); ++ } ++ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { ++ Elf32_Rel *rel = &sec->reltab[j]; ++ rel->r_offset = elf32_to_cpu(rel->r_offset); ++ rel->r_info = elf32_to_cpu(rel->r_info); ++ } ++ } ++} ++ ++ ++static void print_absolute_symbols(void) ++{ ++ int i; ++ printf("Absolute symbols\n"); ++ printf(" Num: Value Size Type Bind Visibility Name\n"); ++ for (i = 0; i < ehdr.e_shnum; i++) { ++ struct section *sec = &secs[i]; ++ char *sym_strtab; ++ int j; ++ ++ if (sec->shdr.sh_type != SHT_SYMTAB) { ++ continue; ++ } ++ sym_strtab = sec->link->strtab; ++ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Sym); j++) { ++ Elf32_Sym *sym; ++ const char *name; ++ sym = &sec->symtab[j]; ++ name = sym_name(sym_strtab, sym); ++ if (sym->st_shndx != SHN_ABS) { ++ continue; ++ } ++ printf("%5d %08x %5d %10s %10s %12s %s\n", ++ j, sym->st_value, sym->st_size, ++ sym_type(ELF32_ST_TYPE(sym->st_info)), ++ sym_bind(ELF32_ST_BIND(sym->st_info)), ++ sym_visibility(ELF32_ST_VISIBILITY(sym->st_other)), ++ name); ++ } ++ } ++ printf("\n"); ++} ++ ++static void print_absolute_relocs(void) ++{ ++ int i, printed = 0; ++ ++ for (i = 0; i < ehdr.e_shnum; i++) { ++ struct section *sec = &secs[i]; ++ struct section *sec_applies, *sec_symtab; ++ char *sym_strtab; ++ Elf32_Sym *sh_symtab; ++ int j; ++ if (sec->shdr.sh_type != SHT_REL) { ++ continue; ++ } ++ sec_symtab = sec->link; ++ sec_applies = &secs[sec->shdr.sh_info]; ++ if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { ++ continue; ++ } ++ sh_symtab = sec_symtab->symtab; ++ sym_strtab = sec_symtab->link->strtab; ++ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { ++ Elf32_Rel *rel; ++ Elf32_Sym *sym; ++ const char *name; ++ rel = &sec->reltab[j]; ++ sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; ++ name = sym_name(sym_strtab, sym); ++ if (sym->st_shndx != SHN_ABS) { ++ continue; ++ } ++ ++ /* Absolute symbols are not relocated if bzImage is ++ * loaded at a non-compiled address. Display a warning ++ * to user at compile time about the absolute ++ * relocations present. ++ * ++ * User need to audit the code to make sure ++ * some symbols which should have been section ++ * relative have not become absolute because of some ++ * linker optimization or wrong programming usage. ++ * ++ * Before warning check if this absolute symbol ++ * relocation is harmless. ++ */ ++ if (is_reloc(S_ABS, name) || is_reloc(S_REL, name)) ++ continue; ++ ++ if (!printed) { ++ printf("WARNING: Absolute relocations" ++ " present\n"); ++ printf("Offset Info Type Sym.Value " ++ "Sym.Name\n"); ++ printed = 1; ++ } ++ ++ printf("%08x %08x %10s %08x %s\n", ++ rel->r_offset, ++ rel->r_info, ++ rel_type(ELF32_R_TYPE(rel->r_info)), ++ sym->st_value, ++ name); ++ } ++ } ++ ++ if (printed) ++ printf("\n"); ++} ++ ++static void walk_relocs(void (*visit)(Elf32_Rel *rel, Elf32_Sym *sym), ++ int use_real_mode) ++{ ++ int i; ++ /* Walk through the relocations */ ++ for (i = 0; i < ehdr.e_shnum; i++) { ++ char *sym_strtab; ++ Elf32_Sym *sh_symtab; ++ struct section *sec_applies, *sec_symtab; ++ int j; ++ struct section *sec = &secs[i]; ++ ++ if (sec->shdr.sh_type != SHT_REL) { ++ continue; ++ } ++ sec_symtab = sec->link; ++ sec_applies = &secs[sec->shdr.sh_info]; ++ if (!(sec_applies->shdr.sh_flags & SHF_ALLOC)) { ++ continue; ++ } ++ sh_symtab = sec_symtab->symtab; ++ sym_strtab = sec_symtab->link->strtab; ++ for (j = 0; j < sec->shdr.sh_size/sizeof(Elf32_Rel); j++) { ++ Elf32_Rel *rel; ++ Elf32_Sym *sym; ++ unsigned r_type; ++ const char *symname; ++ rel = &sec->reltab[j]; ++ sym = &sh_symtab[ELF32_R_SYM(rel->r_info)]; ++ r_type = ELF32_R_TYPE(rel->r_info); ++ ++ switch (r_type) { ++ case R_386_NONE: ++ case R_386_PC32: ++ case R_386_PC16: ++ case R_386_PC8: ++ /* ++ * NONE can be ignored and and PC relative ++ * relocations don't need to be adjusted. ++ */ ++ break; ++ ++ case R_386_16: ++ symname = sym_name(sym_strtab, sym); ++ if (!use_real_mode) ++ goto bad; ++ if (sym->st_shndx == SHN_ABS) { ++ if (is_reloc(S_ABS, symname)) ++ break; ++ else if (!is_reloc(S_SEG, symname)) ++ goto bad; ++ } else { ++ if (is_reloc(S_LIN, symname)) ++ goto bad; ++ else ++ break; ++ } ++ visit(rel, sym); ++ break; ++ ++ case R_386_32: ++ symname = sym_name(sym_strtab, sym); ++ if (sym->st_shndx == SHN_ABS) { ++ if (is_reloc(S_ABS, symname)) ++ break; ++ else if (!is_reloc(S_REL, symname)) ++ goto bad; ++ } else { ++ if (use_real_mode && ++ !is_reloc(S_LIN, symname)) ++ break; ++ } ++ visit(rel, sym); ++ break; ++ default: ++ die("Unsupported relocation type: %s (%d)\n", ++ rel_type(r_type), r_type); ++ break; ++ bad: ++ symname = sym_name(sym_strtab, sym); ++ die("Invalid %s relocation: %s\n", ++ rel_type(r_type), symname); ++ } ++ } ++ } ++} ++ ++static void count_reloc(Elf32_Rel *rel, Elf32_Sym *sym) ++{ ++ if (ELF32_R_TYPE(rel->r_info) == R_386_16) ++ reloc16_count++; ++ else ++ reloc_count++; ++} ++ ++static void collect_reloc(Elf32_Rel *rel, Elf32_Sym *sym) ++{ ++ /* Remember the address that needs to be adjusted. */ ++ if (ELF32_R_TYPE(rel->r_info) == R_386_16) ++ relocs16[reloc16_idx++] = rel->r_offset; ++ else ++ relocs[reloc_idx++] = rel->r_offset; ++} ++ ++static int cmp_relocs(const void *va, const void *vb) ++{ ++ const unsigned long *a, *b; ++ a = va; b = vb; ++ return (*a == *b)? 0 : (*a > *b)? 1 : -1; ++} ++ ++static int write32(unsigned int v, FILE *f) ++{ ++ unsigned char buf[4]; ++ ++ put_unaligned_le32(v, buf); ++ return fwrite(buf, 1, 4, f) == 4 ? 0 : -1; ++} ++ ++static void emit_relocs(int as_text, int use_real_mode) ++{ ++ int i; ++ /* Count how many relocations I have and allocate space for them. */ ++ reloc_count = 0; ++ walk_relocs(count_reloc, use_real_mode); ++ relocs = malloc(reloc_count * sizeof(relocs[0])); ++ if (!relocs) { ++ die("malloc of %d entries for relocs failed\n", ++ reloc_count); ++ } ++ ++ relocs16 = malloc(reloc16_count * sizeof(relocs[0])); ++ if (!relocs16) { ++ die("malloc of %d entries for relocs16 failed\n", ++ reloc16_count); ++ } ++ /* Collect up the relocations */ ++ reloc_idx = 0; ++ walk_relocs(collect_reloc, use_real_mode); ++ ++ if (reloc16_count && !use_real_mode) ++ die("Segment relocations found but --realmode not specified\n"); ++ ++ /* Order the relocations for more efficient processing */ ++ qsort(relocs, reloc_count, sizeof(relocs[0]), cmp_relocs); ++ qsort(relocs16, reloc16_count, sizeof(relocs16[0]), cmp_relocs); ++ ++ /* Print the relocations */ ++ if (as_text) { ++ /* Print the relocations in a form suitable that ++ * gas will like. ++ */ ++ printf(".section \".data.reloc\",\"a\"\n"); ++ printf(".balign 4\n"); ++ if (use_real_mode) { ++ printf("\t.long %lu\n", reloc16_count); ++ for (i = 0; i < reloc16_count; i++) ++ printf("\t.long 0x%08lx\n", relocs16[i]); ++ printf("\t.long %lu\n", reloc_count); ++ for (i = 0; i < reloc_count; i++) { ++ printf("\t.long 0x%08lx\n", relocs[i]); ++ } ++ } else { ++ /* Print a stop */ ++ printf("\t.long 0x%08lx\n", (unsigned long)0); ++ for (i = 0; i < reloc_count; i++) { ++ printf("\t.long 0x%08lx\n", relocs[i]); ++ } ++ } ++ ++ printf("\n"); ++ } ++ else { ++ if (use_real_mode) { ++ write32(reloc16_count, stdout); ++ for (i = 0; i < reloc16_count; i++) ++ write32(relocs16[i], stdout); ++ write32(reloc_count, stdout); ++ ++ /* Now print each relocation */ ++ for (i = 0; i < reloc_count; i++) ++ write32(relocs[i], stdout); ++ } else { ++ /* Print a stop */ ++ write32(0, stdout); ++ ++ /* Now print each relocation */ ++ for (i = 0; i < reloc_count; i++) { ++ write32(relocs[i], stdout); ++ } ++ } ++ } ++} ++ ++static void usage(void) ++{ ++ die("relocs [--abs-syms|--abs-relocs|--text|--realmode] vmlinux\n"); ++} ++ ++int main(int argc, char **argv) ++{ ++ int show_absolute_syms, show_absolute_relocs; ++ int as_text, use_real_mode; ++ const char *fname; ++ FILE *fp; ++ int i; ++ ++ show_absolute_syms = 0; ++ show_absolute_relocs = 0; ++ as_text = 0; ++ use_real_mode = 0; ++ fname = NULL; ++ for (i = 1; i < argc; i++) { ++ char *arg = argv[i]; ++ if (*arg == '-') { ++ if (strcmp(arg, "--abs-syms") == 0) { ++ show_absolute_syms = 1; ++ continue; ++ } ++ if (strcmp(arg, "--abs-relocs") == 0) { ++ show_absolute_relocs = 1; ++ continue; ++ } ++ if (strcmp(arg, "--text") == 0) { ++ as_text = 1; ++ continue; ++ } ++ if (strcmp(arg, "--realmode") == 0) { ++ use_real_mode = 1; ++ continue; ++ } ++ } ++ else if (!fname) { ++ fname = arg; ++ continue; ++ } ++ usage(); ++ } ++ if (!fname) { ++ usage(); ++ } ++ regex_init(use_real_mode); ++ fp = fopen(fname, "r"); ++ if (!fp) { ++ die("Cannot open %s: %s\n", ++ fname, strerror(errno)); ++ } ++ read_ehdr(fp); ++ read_shdrs(fp); ++ read_strtabs(fp); ++ read_symtabs(fp); ++ read_relocs(fp); ++ if (show_absolute_syms) { ++ print_absolute_symbols(); ++ return 0; ++ } ++ if (show_absolute_relocs) { ++ print_absolute_relocs(); ++ return 0; ++ } ++ emit_relocs(as_text, use_real_mode); ++ return 0; ++} +diff --git a/scripts/Makefile b/scripts/Makefile +index df7678f..3626666 100644 +--- a/scripts/Makefile ++++ b/scripts/Makefile +@@ -8,6 +8,8 @@ + # conmakehash: Create arrays for initializing the kernel console tables + # docproc: Used in Documentation/DocBook + ++HOST_EXTRACFLAGS += -I$(srctree)/tools/include ++ + hostprogs-$(CONFIG_KALLSYMS) += kallsyms + hostprogs-$(CONFIG_LOGO) += pnmtologo + hostprogs-$(CONFIG_VT) += conmakehash