Subject: Re: modules without a .toc section not loading

From: Alan Modra <[email-masked]>
Date: Thu, 20 Apr 2006 18:24:01 +0930

On Wed, Apr 19, 2006 at 05:54:45PM -0700, Robin H. Johnson wrote:
> On Thu, Apr 20, 2006 at 09:58:30AM +0930, Alan Modra wrote:
> > > Ideas for Solutions:
> > > 1. Either the kernel needs to realize that it does not need a .toc
> > > section (I'm uncertain about this, being new to PPC).
> > > 2. GCC is at fault, and it should be generating a .toc.
> > The former.
> Ok, so in module_64.c, under what cases is .toc NOT needed?
> 
> I dug at the code, and the R_PPC_REL24 blocks need the contents of the
> .toc (via stub_for_addr -> create_stub -> my_r2), and I'm not certain
> about it.

Yes, the stubs need some reasonable r2 value.

> The R_PPC64_TOC/R_PPC64_TOC16/R_PPC64_TOC16_DS blocks shouldn't be hit
> when there isn't a .toc* from what I figure.

Yes, and finding such a reloc should trigger an error.

> If the R_PPC_REL24 block won't be hit when there isn't a .toc, the check
> in module_frob_arch_sections, lines 194-197 can just be shortened as a
> fix to the problem.

You could have an R_PPC_REL24 in a module (a function call into another
module or the kernel) without a .toc.  A module without a .toc section
won't itself use r2 for anything, but the any stubs added do need r2.

I'm surprised no one has fixed this.  Here's a totally untested patch
that ought to do the trick (or if it doesn't, should put you on the
right path).

--- linux-2.6.16/arch/powerpc/kernel/module_64.c~	2006-01-18 14:17:29.000000000 +1030
+++ linux-2.6.16/arch/powerpc/kernel/module_64.c	2006-04-20 18:22:42.000000000 +0930
@@ -191,8 +191,15 @@ int module_frob_arch_sections(Elf64_Ehdr
 				 (void *)hdr
 				 + sechdrs[sechdrs[i].sh_link].sh_offset);
 	}
-	if (!me->arch.stubs_section || !me->arch.toc_section) {
-		printk("%s: doesn't contain .toc or .stubs.\n", me->name);
+
+	/* If we don't have a .toc, just use .stubs.  Nothing in the
+	   module itself should use r2, but we need to set r2 in case
+	   the module calls out to other functions. */
+	if (!me->arch.toc_section)
+		me->arch.toc_section = me->arch.stubs_section;
+
+	if (!me->arch.stubs_section) {
+		printk("%s: doesn't contain .stubs.\n", me->name);
 		return -ENOEXEC;
 	}
 
@@ -338,11 +345,21 @@ int apply_relocate_add(Elf64_Shdr *sechd
 			break;
 
 		case R_PPC64_TOC:
+			if (me->arch.toc_section == me->arch.stubs_section) {
+				printk("%s: TOC reloc without .toc\n",
+				       me->name);
+				return -ENOEXEC;
+			}
 			*(unsigned long *)location = my_r2(sechdrs, me);
 			break;
 
 		case R_PPC64_TOC16:
-			/* Subtact TOC pointer */
+			if (me->arch.toc_section == me->arch.stubs_section) {
+				printk("%s: TOC16 reloc without .toc\n",
+				       me->name);
+				return -ENOEXEC;
+			}
+			/* Subtract TOC pointer */
 			value -= my_r2(sechdrs, me);
 			if (value + 0x8000 > 0xffff) {
 				printk("%s: bad TOC16 relocation (%lu)\n",
@@ -355,7 +372,12 @@ int apply_relocate_add(Elf64_Shdr *sechd
 			break;
 
 		case R_PPC64_TOC16_DS:
-			/* Subtact TOC pointer */
+			if (me->arch.toc_section == me->arch.stubs_section) {
+				printk("%s: TOC16_DS reloc without .toc\n",
+				       me->name);
+				return -ENOEXEC;
+			}
+			/* Subtract TOC pointer */
 			value -= my_r2(sechdrs, me);
 			if ((value & 3) != 0 || value + 0x8000 > 0xffff) {
 				printk("%s: bad TOC16_DS relocation (%lu)\n",

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre