lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


consider these:

main.c:
#include<stdio.h>
main(){printf("Hello from main\n");func1();printf("Back to main\n");func2();printf("Back to main\n");}

func1.c:
#include<stdio.h>
func1(){printf("\tHello from func1\n");func2();printf("\tBack to func1\n");}

func2.c:
#include<stdio.h>
func2(){printf("\t\tHello from func2\n");}

myfunc2.c:
#include<stdio.h>
func2(){printf("\t\Hello from myfunc2\n");}

Try creating a non-shared library containing func1.o and func2.o : OK, no problem, there's no duplicate symbol. You can then link main.o with it to create the main program.
Try creating a non-shared library containing func1.o and func2.o and myfunc2.o : error, duplicate symbol (func2). You cannot link main.o with it to create the main program.

Try creating a **shared** library "mylib.so" containing func1.o and func2.o : OK, no problem, there's no duplicate symbol. You can then link main.o with it to create the main program.
Now try linking main.o with myfunc2.o and mylib.so, what you get is this:

Hello from main
    Hello from func1
        Hello from func2 (sic!)
    Back to func1
Back to main
    Hello from myfun2 (sic!)
Back to main

In other words, the program is linked with two distinct versions of func2(), which exist simultaneously! One version is statically used by func1() from inside the sharedlibrary where func1 is used by main. The second version (myfunc1.o) is however is now used by main but does **not** replace the version used internally by func1() inside the shared library (even if mylib.so also exports the symbol for "func2", this export is not used when linking main because you've specified myfunc2.o **before** mylib.so when linking main).

You cannot then replace the internal use made inside the shared libary because the binding from func1 to func2 is already resolved internally in the shared library, and the dependency of func1 with func2 is not exposed: the shared libary only exposes a set of exported symbols.

You'd have the same situation with DLLs on Windows.

DLLs or shared libary are much more flexible and predictable than simple libaries where no early binding occurs between units. Basically, the simple library is just an archive containing multiple units, in an unspecified order, so they cannot export the same symbol multiple times. If the archive format allows it, it's just because it ignores compeltley the exported symbols in each unit but only distinguish the unit names packed in the library. The situation is different with shared libaries whose internal links between units are already resolved by early binding, and these early binding cannot be replaced.

What is not predictable is the order of units inside **non-shared** libraries (that's why a shared libary cannot be made containing the same symbol multiple times from distinct units in the library). But there's no problem for the order of units inside a call to a linker. An no problem inside shared libaries, because they are warrantied to export the same symbol only once and have their dependencies already resolved inside it, so they cannot export the same symbol multiple times.

A shared libary (or executable) however can be build to contain the same symbol multiple times, but only one of these symbols is really exported: the first one specified when linking it, but it does not mean that there are not multiple implementations the shared library (or program) however becomes an unbreakable new unit (that Windows calls a "module" if it is executable, but this also applies to shared libaries on Linux using the ELF binary format or similar, where early binding of internal symbols is already made inside it and where sets of exported symbols are already merged to a single set containing no duplicate, even if there are multiple internal implementations).





Le sam. 24 nov. 2018 à 13:07, Viacheslav Usov <via.usov@gmail.com> a écrit :
On Sat, Nov 24, 2018 at 12:28 AM Sean Conner <sean@conman.org> wrote:

> And I think this is where we have our differenes.  If the "current translation" does not contain function foo(), *then* such a function is looked for in any given libraries [1].

Correct. As long as there is exactly one definition of foo() in the entire program, this is well-defined.

Having bar() defined in the "current translation" and some other library would cause undefined behaviour, where your reasoning would no longer apply.

>  Again, the key sentence from 5.1.1.2 #8:  Library components

This is not where an "entire program" is referenced.

> I could not find the phrase "full library" (or variations) in the C99 Standard.

I used the word "full" to emphasise that libraries are not considered piece-wise by the standard when it talks about an "entire program".

> Citation please, from the C standard (any one of C89, C99, or C11).

Given that neither the notion of static vs shared libraries, not their organisation, is addressed by the standard, any discussion at this level is not portable, and any use of implementation details, in situations that cause undefined behaviour in the standard, even if it is consistent within the Implementation, is non-conformant. I stress the "use" not the "details".

> It's odd to think of GNU C as not being conformant.

I did not say GNU C was non-coformant (even though it may be). As I explained in earlier messages in this thread, it is the "program", not the "tool", that is non-conformant if the one definition requirement is violated.

> I did some experiments last night on this very subject. 

And, as far as I can tell, they were consistent with my description, wherein the linker pulls previously archived object files from a static library, ignoring other object files whenever it can. Which is perfectly fine when there is no more than one definition of any external symbol in the entire program, and is a trivially acceptable form of undefined behaviour otherwise.

> At first, I used Linux but curious, I went and did the same experiments on Solaris

As mentioned by others, Linux and Solaris have a common Unix heritage and adhere to other common standards, so one should expect similar behaviours. So as a demonstration of portability, this is quite weak.

We have an elephant in the room here, and let me just name it: Windows. Once somebody demonstrates similar techniques on Windows, we could talk about real-world portability.

> They are not pre-linked executables.  Well, mostly.  I know you can execute libc under Linux:

To execute means more than just run something like a program in a shell. And an executable means more than the main executable program file. Any, anyway, the emphasis should have been on "pre-linked" rather than "executable".

> Wow!  It worked!  Even with func1() and func2() in the same translation unit, func1() is calling func2() from translation unit myfunc2.  And it's not Linux! (for the record, it worked under Linux).

Great. Now you might go back to my response to Luiz and address concerns "First" and "Second" there.

> I'm not reading "undefined behavior" there, I see "error" there.

There is no "error" in the standard. What you call an "error", is "undefined behaviour", because "If a ‘‘shall’’ or ‘‘shall not’’ requirement [...] is violated, the behavior is undefined", which I said in my first message in this thread.

Cheers,
V.