Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ensure the uniquesness of global symbols and restric its scope in C?

Tags:

c

linux-kernel

Below is the trick used in Linux Kernel for per-cpu valuables. As the comment says, it could acheive these two goals:

1.enforce scope.

2.ensure uniqueness, even the static ones.

Here is how the magic plays(For simplicity, I subsititude some MACRO's) :

/*
 * __pcpu_scope_* dummy variable is used to enforce scope.  It
 * receives the static modifier when it's used in front of
 * DEFINE_PER_CPU() and will trigger build failure if
 * DECLARE_PER_CPU() is used for the same variable.
 *
 * __pcpu_unique_* dummy variable is used to enforce symbol uniqueness
 * such that hidden weak symbol collision, which will cause unrelated
 * variables to share the same address, can be detected during build.
 */
 #define DECLARE_PER_CPU_SECTION(type, name, sec)                        \
 extern __attribute__((section(".discard"), unused))                     \
               char __pcpu_scope_##name;                                 \
 extern __attribute__((section(sec))) __typeof__(type) name

 #define DEFINE_PER_CPU_SECTION(type, name, sec)                         \
 __attribute__((section(".discard"), unused)) char __pcpu_scope_##name;  \
 extern __attribute__((section(".discard"), unused))                     \
             char __pcpu_unique_##name;                                  \
 __attribute__((section(".discard"), unused)) char __pcpu_unique_##name; \
 __attribute__((section(sec)))  __attribute__((weak))                    \
              __typeof__(type) name

My questions are

  1. For Goal #1. How could it enforce scope? Does it work like this:

    When DECLARE* and DEFINE* exist in the same traslation unit, it turns the variable in question to internal linkage, and thus, any extra DECLARE* for the same variable will trigger build failure(cause they disagree on linkage)

    But if this is true, then

    • How the interal-linkage-receivement works ? From C99 6.9.2.2(External object definitions), this happens only for tentative definition, but this case seems not tentative definition?
    • Wont't this break the "One-definition-and-mutiple-declarations-are-OK" rule?
  2. For Goal #2, the two __pcpu_unique_##name decalation(exactly, one is declaration, the other one is definition) seems play the same trcik as the __pcpu_scope_##name, then how it help to ensure the uniqueness ?

FYI, the code in question could be viewed here: http://lxr.linux.no/linux+v3.9/include/linux/percpu-defs.h#L61

like image 423
larmbr Avatar asked Oct 05 '22 06:10

larmbr


1 Answers

I don't think this is enforced by the compiler. See the ARM vmlinux.lds.S, vmlinux.lds.h and percpu.h; the linker file is pre-processed to use kernel configuration variables. The ".discard" sections are a bunch of symbols that will cause a link conflict on multiple definitions; but they are thrown away and do not make it into a binary.

See the macros PER_CPU, PER_CPU_FIRST, PER_CPU_SHARED_ALIGNED, etc. These macros only take a type and name.

Your macro expansion is not quite correct.

 #define DECLARE_PER_CPU_SECTION(type, name, sec)                        \
 extern __attribute__((section(".discard"), unused))                     \
               char __pcpu_scope_##name;                                 \
 extern __attribute__((section(PER_CPU_BASE_SECTION sec))) __typeof__(type) name

The sec parameters is actually a sub-section; note PER_CPU_BASE_SECTION and the string concatenation.

As I understand it, It receives the static modifier... seems misleading. It would be better expressed as If it receives.... So these cases are different,

static DEFINE_PER_CPU_PAGE_ALIGNED(... /* Cause error with DECLARE */
DEFINE_PER_CPU(unsigned int, irq_count) = -1; /* Fine with DECLARE */

In any case, this is not being enforced by the compilers, but by the GNU linker (or at least both) as it is important that these values are cache aligned and in some cases the cache size is configured by the kbuild infrastructure.

It is also important to note a preceding comment related to code generation,

 * s390 and alpha modules require percpu variables to be defined as
 * weak to force the compiler to generate GOT based external
 * references for them.  This is necessary because percpu sections
 * will be located outside of the usually addressable area.
 * This definition puts the following two extra restrictions when
 * defining percpu variables.
 *
 * 1. The symbol must be globally unique, even the static ones.
 * 2. Static percpu variables cannot be defined inside a function.
 *
 * Archs which need weak percpu definitions should define
 * ARCH_NEEDS_WEAK_PER_CPU in asm/percpu.h when necessary.

Ie, the __attribute__((weak)) is being used not for symbol resolution but for code generation side effects. There is an #else condition where the weak complication is not bothered with.

Now to answer your questions briefly,

For Goal #1. How could it enforce scope?

Both a static and non-static declaration result when we have,

DECLARE_PER_CPU_PAGE_ALIGNED(int,foobar);
static DEFINE_PER_CPU_PAGE_ALIGNED(int,foobar);

For Goal #2, ... __pcpu_unique_##name, then how it help to ensure the uniqueness ?

Multiple weak symbols do not cause errors. The __pcpu_unique_##name is not weak so it is being used to enforce uniqueness as weak is being used for code generation reasons.

See Gcc's function attributes and search for weak, in case the normal purpose of weak is not understood.

like image 184
artless noise Avatar answered Oct 10 '22 02:10

artless noise