C++ Logo

std-discussion

Advanced search

Re: Global array of objects over multiple files

From: Thiago Macieira <thiago_at_[hidden]>
Date: Mon, 21 Oct 2024 09:24:07 -0700
On Sunday 20 October 2024 22:33:07 Pacific Daylight Time Federico Kircheis via
Std-Discussion wrote:
> > Indeed. You're quoting to me what can happen under ODR violations.
>
> Saying to each other "you are wrong" does not really help.
> Could you help me find the part of the standard that say that what I
> wrote is UB?

Nowhere. It's not UB.

It's ill-formed. That's not the same thing. Jens' reply has it. See
https://eel.is/c++draft/basic.def.odr#15

Your examples match the conditions: the variable in question (a definable item)
is not an inline function or variable and is defined in multiple translation
units without any of the exclusions. That paragraph says your program is ill-
formed, no diagnostic required (IFNDR).

I've reduced it so you can see what's wrong. ALL your examples are equivalent
to this: https://godbolt.org/z/rYnrcToTn

> > The difference here is that int is a primitive, so nothing gets emitted in
> > the first place, unless its address is taken. In that case, it's emitted
> > as a global and there is a problem.
>
> I wrote int to make a minimal example, I should have written
> std::string, but it should not make any difference.

It DOES make a difference because of the const. See Jens' reply.

If you remove it, it reduces to the example I gave above and is IFNDR.

> > Yes. But you changed how you compile and how many times you link lib0.cpp
> > into your executable. If you fix your problem, then the problem is not
> > there any more.
>
> So, what was the problem?
> According to you, not the fact that I used libraries...

It's the fact that you linked lib0.cpp twice into your executable, by way of
the dynamic linking.

My example above is not that, but both lib1.cpp and lib2.cpp have a symbol
with the same name linked into the final executable, which is IFNDR. Here's an
even simpler example showing "the same file" compiled twice:
https://godbolt.org/z/vEE4bqq68

> > Yes, I am reading this as "if I remove the problematic build solution and
> > fix the issue". I agree that in this case the problem is solved.
>
> So, the problem was not in my *code*?

It's the combination of the code and how you compile/link it. Each TU alone is
fine and has no UB. But the combination of them into the final executable is
ill-formed.

Does your CMakeLists.txt count as "your code"?

> > Agreed. Strictly speaking, it's the combination of lib1 and lib2 into one
> > executable. You can't do that.
>
> And how does the author of main know?

The same way as any other bug: inspect the symptoms, debug it, and find out.
This is an IFNDR: *no diagnostic required* on the toolchain, so it is not
required to help you. You must find out how your code is ill-formed from the
fact it misbehaves and fix it.

> >> You can obviously report a bug to those libraries to hide their symbols
> >> with compiler specifics tools, but according to the standard everything
> >> is fine.
> >
> > Sure. They *chose* to have a global symbol here (there's no "static").
> > That
> > means they are claiming "answer_of_life" as part of their ABI. Two TUs
> > can't do that at the same time.
>
> They choose to have a global variable that is not reachable in any way
> in C++ outside of its TU.

Stop. The variable *can* be reached outside of the TU because they "forgot"
the static keyword. Therefore, it is a global symbol and they are claiming
that as part of their ABI. They claim it exclusively: no other library can
define the same symbol.

> If you use extern, you cannot use static.

Yes, if you want it as a global symbol, you use extern.

> > If they didn't mean to claim that, then there are solutions to avoid the
> > collision, like namespaces.
>
> I left namespace out of brevity, if the author of lib0 used a namespace,
> it wouldn't make any difference.

No, it wouldn't make a difference, because it's the same source file twice.
Because lib1 and lib2 include lib0 without hiding its symbols, they
necessarily include all of lib0's ABI in their own ABI. That means lib1 and
lib2 have clashing ABI. Someone has to resolve that.

The fact that lib1 and lib2 authors left it up to you is your problem. You
chose to use those two libraries with questionable quality. As I said, using
hidden visibility is *mandatory* and they failed to hide the symbols that
weren't part of their own ABI.

> > Indeed. Like I said, Dynamic Shared Objects 101 is mandatory reading.
>
> Can you provide a link to me?

https://www.akkadia.org/drepper/dsohowto.pdf

Ulrich Drepper is a former maintainer of glibc, so you should trust him in his
expertise. But by necessity this paper is focused on C.

He's also the author of the specification that gave us proper __thread and
thread_local variables on Unix systems.

Personally, I claim that it's slightly outdated: today, you should use
protected visibility for your exported symbols and -mno-direct-extern-access,
but that option is still too new for ubiquitous use.

> > The problem in your case is the presence of lib0 inside of lib1 and lib2.
> > Stop propagating this horrible practice of bundling copies of third-party
> > content.
> What do you mean by vendoring an bundling?

The copy of a third party source code inside of another is "vendoring". The
resulting code is bundled inside of the library.

Stop vendoring: instead, just build the third-party library using their own
build system and install to your target build environment. And never link a
dynamic library to a static library (unless that's a "convenience library"
that is also part of your project).

> The are not precompiled or no not have a separate copy of lib0 in they
> sources.

There's a bundled copy of lib0 inside of each of lib1 and lib2. That means you
CANNOT load both lib1 and lib2 into memory in the same process because that's
ill-formed (no diagnostic required).

> The libraries have a common dependency.
> It is not that uncommon, or should a library used at mostly once worldwide?

Once per process address space.

-- 
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
  Principal Engineer - Intel DCAI Platform & System Engineering

Received on 2024-10-21 16:24:12