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


2017-08-05 2:03 GMT+08:00 Orlando Arias <orlandoarias@gmail.com>:
> 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.

I think I finally figure this out what you mean:

when the computer try to call `open_hook' inside `open'(as in the second
example), it jumps to where `open_hook' is pointing to, which is known
when the .so is built, and does the execution. So we are fine here. But in
the first example, the GOT only know where `open' lives, not where it points
to, so the computer jump to where `open' lives, only to find that there is
nothing there to be execute, thus crashing.

Many thanks!

>>> 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.

Yes, the dynamic linker has two options: 1) find the address of the symbol
and place that address into the GOT; 2) find the address of the symbol and
if that symbol is a pointer, dereference it, and then put the dereferenced
value in the GOT. So there is ambiguity here and the dynamic linker choose
the first option, unfortunately (for me).

> 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.

Many thanks!
Yubin


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