All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [parisc-linux] userspace function pointers in the kernel
@ 2000-09-13  1:05 Cary Coutant
  2000-09-13  3:27 ` David Huggins-Daines
  0 siblings, 1 reply; 12+ messages in thread
From: Cary Coutant @ 2000-09-13  1:05 UTC (permalink / raw)
  To: David Huggins-Daines; +Cc: parisc-linux, Alan Modra

>It looks like the IA-64 runtime achieves this by generating official
>procedure descriptors for every defined function in an object and
>placing them in the .opd section, then using these descriptors
>whenever the address of a function is taken.  Presumably external
>functions are compared using their .IA_64.pltoff entries?  I'm not
>clear on how that works and don't have an IA-64 handy to experiment.

I can't say for sure how Linux/IA-64 uses the .opd section, but the 
runtime and the psABI recommend that the "official" function descriptors 
be created by the dynamic loader as needed in response to dynamic FPTR 
relocations. A function pointer is always loaded from the GOT or from a 
statically-initialized variable, either of which would have been tagged 
with a dynamic FPTR relocation. The dynamic loader will allocate one and 
only one OFD for each function whose address is taken, and resolve each 
function pointer to the address of that OFD. Function pointers can then 
be compared as simple 32- or 64-bit quantities, without worrying about 
the contents of the function descriptor that they point to.

One way of avoiding the dynamic allocation by the dynamic loader is for 
the linker to allocate OFDs statically for each exported function, and 
for each hidden or static function whose address is taken. It looks like 
this may be what Linux is doing on IA-64. The disadvantage is that you 
end up with far more OFDs than you'll ever need, since few functions ever 
really have their addresses taken.

The .pltoff section is for local copies of OFDs that are used by inlined 
import stubs. When the compiler decides that a function is unlikely to 
resolve within the load module, it generates an inlined external call 
sequence, rather than rely on the linker to resolve a direct call to an 
import stub. This inlined sequence needs a local copy of the function 
descriptor to avoid an extra indirection. Since these function 
descriptors never serve as an official function descriptor, it doesn't 
matter that they don't have the same address as an OFD.

Note that the same argument applies to C++ vtables -- we can put copies 
of the function descriptors there, too, since we don't need the pointers 
to vtable entries to be unique.

As an aside, the problem we had on HP-UX/PA was in statically allocating 
the official function descriptors -- the linker allocated OFD 
"candidates" whenever it saw the address of a function get taken. This 
produced multiple candidates for the OFD, and it was possible that none 
of those candidates were actually in the load module where the function 
was defined. Thus, the loader had to choose one arbitrarily, and 
sometimes could be tricked into choosing different ones at different 
times. Hence the problem.

-cary

^ permalink raw reply	[flat|nested] 12+ messages in thread
* Re: [parisc-linux] userspace function pointers in the kernel
@ 2000-09-13 17:11 Cary Coutant
  2000-09-14  0:32 ` Alan Modra
  0 siblings, 1 reply; 12+ messages in thread
From: Cary Coutant @ 2000-09-13 17:11 UTC (permalink / raw)
  To: Alan Modra, David Huggins-Daines; +Cc: parisc-linux

>The model I'm thinking of for elf32-hppa is along these lines:
>
>o  The linker creates a plt entry for all plabel relocs.  plt entries for
>   elf32-hppa are a function address, linkage table pointer pair, so
>   there's no need for the dynamic linker to allocate fptrs.
>o  A dynamic plabel reloc will have the function symbol, and an addend
>   into the plt.  This is a rather unusual reloc because the function
>   symbol value is ignored when calculating the final value.
>o  The dynamic linker builds a list or hash table of function symbols
>   versus plt offsets, and adjusts plabels so that only one plt entry is
>   ever used per function.

This is close to how it works on HP-UX, but we didn't use the table in 
your third bullet, so it became possible for the dynamic loader to come 
up with one function descriptor (PLT entry) one time, and a different one 
the next time.

Another thing you need to watch out for is when a library is dlclosed. 
Because the PLT entries are allocated when you see the reference -- not 
the definition -- you may not have a candidate PLT entry in the load 
module that contains the definition of the function (unless you also 
allocate PLT entries for all exported symbols). This means that you might 
be asked to dlclose a library that contains the PLT entry that is 
currently in use as the official function pointer for a function in 
another load module. If you go ahead and deallocate the library, you're 
hosed.

-cary

^ permalink raw reply	[flat|nested] 12+ messages in thread
[parent not found: <873dj5mfmb.fsf@linuxcare.com>]
* Re: [parisc-linux] userspace function pointers in the kernel
@ 2000-09-12 20:54 Cary Coutant
  2000-09-12 21:22 ` David Huggins-Daines
  0 siblings, 1 reply; 12+ messages in thread
From: Cary Coutant @ 2000-09-12 20:54 UTC (permalink / raw)
  To: David Huggins-Daines, parisc-linux

>Next, even if we *weren't* trapping we would still be doing the wrong
>thing, because $$sh_func_adrs is actually supposed to give us the
>address of the function's code, and it isn't going to do this since
>the function lives in a different space.
>
>In the case of sigaction() I think we actually want to compare the
>plabel value (rather than the code address) againsr SIG_IGN and
>SIG_DFL.  However there may be other cases where this will break.
>
>Yes this is yet another side effect of the broken PA-RISC run time
>architecture.

I'm deeply troubled to learn that Linux is using $$sh_func_adrs, as I 
will readily agree with you that it's seriously broken.

It was introduced as a way to compare function pointers reliably, but in 
fact accomplishes exactly the opposite. The problem is that our dynamic 
loader cannot guarantee that two function pointers that both refer to the 
same procedure will compare equal. Rather than fix the dynamic loader, we 
chose to modify the compilers to compare two function pointers by digging 
down into the function descriptor itself to see if they really refer to 
the same function. Unfortunately, not all data items typed as function 
pointers are really valid pointers, and they can't be dereferenced 
safely. Thus, the millicode routine uses the probe instruction to verify 
that the address can be dereferenced without causing a memory fault.

Of course, this isn't 100% reliable on HP-UX, either, since a probe 
failure doesn't mean that the address isn't mapped. It should also be a 
"probe,r" instead of "probe,w".

For Linux, I strongly recommend that the dynamic loader guarantee that 
there is a 1-1 mapping between function and function pointer. If you do 
this, you won't need this silly special case when comparing function 
pointers.

>What do the IA-64 people do about this problem?

On IA-64, the dynamic loader will maintain a 1-1 mapping.

-cary

^ permalink raw reply	[flat|nested] 12+ messages in thread
* [parisc-linux] userspace function pointers in the kernel
@ 2000-09-12 20:12 David Huggins-Daines
  0 siblings, 0 replies; 12+ messages in thread
From: David Huggins-Daines @ 2000-09-12 20:12 UTC (permalink / raw)
  To: parisc-linux

Hi,

This simple program:

#include <signal.h>

void foo(int signo)
{
	psignal(signo, "foo");
}

int main()
{
	signal(SIGFPE, foo);
	return 0;
}

Now causes the kernel (latest from CVS) to crash where it didn't
before:

Kernel Fault: Code=15 regs=c7c44600 (Addr=00002c04)

PSW  : 0006ff0a  GR 1 : c0233000  GR 2 : c011dcf0  GR 3 : c7c44518  
GR 4 : 20020350  GR 5 : 00000008  GR 6 : 00000007  GR 7 : 00000008  
GR 8 : c7c44600  GR 9 : 2002114c  GR10 : 20020670  GR11 : 00000040  
GR12 : 00000001  GR13 : 00000078  GR14 : 00057000  GR15 : 00057000  
GR16 : 00000063  GR17 : 00000020  GR18 : 2002058c  GR19 : 00000000  
GR20 : 00002c06  GR21 : c7f2387c  GR22 : fffffff8  GR23 : c7f23880  
GR24 : c7c44528  GR25 : c7c44518  GR26 : 00002c04  GR27 : c027a000  
GR28 : fffffff2  GR29 : 00002c06  GR30 : c7c44840  GR31 : c011d7c8  
SR0  : 00002002  SR1  : 00002002  SR2  : 00000000  SR3  : 00002002  
SR4  : 00000000  SR5  : 00000000  SR6  : 00000000  SR7  : 00000000  

IASQ : 00000000 00000000 IAOQ : c02336a0 c02336a4 ORIG_R28 : 00003fff
 IIR : 075f11d6 ISR : 00000000 IOR : 00002c04

Which is:

c0233698 <$$sh_func_adrs>:
c0233698:       37 5d 00 00     ldo 0(r26),ret1
c023369c:       d7 40 0c 3f     depw r0,30,1,r26
c02336a0:       07 5f 11 d6     probe,w (sr0,r26),r31,r22
                                ^^^^^^^^^^^^^^^^^^^^^^^^^  this insn
c02336a4:       d2 d6 3b ff     extrw,u,= r22,31,1,r22
c02336a8:       0f 40 10 9d     ldw  0(sr0,r26),ret1
c02336ac:       eb e0 c0 02     bv,n r0(r31)

Following the millicode return pointer takes us to do_signal() in
kernel/signal.c, somewhere around these lines:

		if (k->sa.sa_handler == SIG_IGN
		    || (k->sa.sa_handler == SIG_DFL

Note that the kernel is trying to compare a function pointer passed in
from userspace with an arbitrary value.

Now, first of all, this means our DTLB miss handler is broken with
respect to the PROBE instructions.  According to the architecture
manual,

        Notes: If this instruction causes a non-access data TLB miss
        fault/non-access data page fault, the operating system's
        handler is required to search its page tables for the given
        address. If found, it does the appropriate TLB insert and
        returns to the interrupting instruction. If not found, the
        handler must decode the target field of the instruction, set
        that GR to 0, set the IPSW[N] bit to 1, and return to the
        interrupting instruction.

Obviously that's not what's happening here.

Next, even if we *weren't* trapping we would still be doing the wrong
thing, because $$sh_func_adrs is actually supposed to give us the
address of the function's code, and it isn't going to do this since
the function lives in a different space.

In the case of sigaction() I think we actually want to compare the
plabel value (rather than the code address) againsr SIG_IGN and
SIG_DFL.  However there may be other cases where this will break.

Yes this is yet another side effect of the broken PA-RISC run time
architecture.  What do the IA-64 people do about this problem?

In the worst case scenario we may have to hack our millicode in the
kernel somewhat to deal with this.

-- 
dhd@linuxcare.com, http://www.linuxcare.com/
Linuxcare. Support for the revolution.

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2000-09-14  0:34 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2000-09-13  1:05 [parisc-linux] userspace function pointers in the kernel Cary Coutant
2000-09-13  3:27 ` David Huggins-Daines
2000-09-13  4:06   ` Alan Modra
2000-09-13 22:04     ` Function descriptors, etc. (was Re: [parisc-linux] userspace function pointers in the kernel) David Huggins-Daines
2000-09-14  0:22       ` Alan Modra
  -- strict thread matches above, loose matches on Subject: below --
2000-09-13 17:11 [parisc-linux] userspace function pointers in the kernel Cary Coutant
2000-09-14  0:32 ` Alan Modra
     [not found] <873dj5mfmb.fsf@linuxcare.com>
2000-09-13  0:59 ` Alan Modra
2000-09-12 20:54 Cary Coutant
2000-09-12 21:22 ` David Huggins-Daines
2000-09-13  0:23   ` Alan Modra
2000-09-12 20:12 David Huggins-Daines

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.