This is the mail archive of the binutils@sourceware.org mailing list for the binutils 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]

Re: ld.so binding time


Greetings,

>> Even if it was mapped as executable, it would still be undefined behaviour,
>> since there is no actual code there to be executed.
> 
> That I don't understand. If "there is no actual code there to be executed", how
> could my second example (i.e., the `open_hook') succeed? You see, the
> `open_hook' pointer is changed in the __attribute__((constructor)), dynamically.

In your second example, you have this:

open_t open_hook = NULL;
/* some code that initializes open_hook */
int open(const char *pathname, int flags, ...)
{
    ...
    open_hook(...);
    ...
}

When your code calls open(), the dynamic linker will try to resolve the
symbol for open. It uses the function you declared there:

int open(const char* pathname, int flags, ...)

There is code in that function: loading what is contained in the pointer
open_hook into a register, then issuing an indirect call to it [or
indirect jump if tail call optimizations took effect]. The address space
that contains the function open() is in the .text segment of your
library, which gets mapped as executable. Since the dynamic linker
resolves the address of a function and places that into the GOT,
execution takes place as expected.

>> And no, you can not make ld.so dereference the pointer as you want it
>> to. Otherwise, it would not be able to resolve other symbols of similar
>> types as it would be expected to do.
> 
> Hmm... Not quite understand. If would be better if you can provide
> some examples.

In contrast, your original code says:

int (*open)(const char*, int, ...);

/* code that initializes open to the address of some_function() */

int some_function(const char* pathname, int flags, ...) {
	/* stuff */
}

Now the symbol open is no longer resolves to a function, but to a
variable [of pointer type]. When you issue a call to open() in your
application, what gets placed in the GOT is the address of that symbol
[the pointer variable, not what the variable is pointing to]. When the
indirect jump in the PLT gets executed, the program counter ends up in
non-executable memory, so you crash.

What I tried to mention above is that you can't have the dynamic linker
dereference your pointer [open] in this case, and place the dereferenced
address [the address of some_function()] in the GOT. If you do that, you
are breaking the way the dynamic linker works. The basic example is when
you expect a pointer to actually be resolved, instead of what the
pointer is dereferecing. The more complex caveat is that compiled code
loses type information. There is no real way for the machine to
differentiate the type of open from any other integer value.

Cheers,
Orlando.

Attachment: signature.asc
Description: OpenPGP digital signature


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