Public symbol for private llvm variables in LLC

When compiling:

; ModuleID = 'Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba'
source_filename = "Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba"
target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"
target triple = "arm64-pc-windows-msvc"

@0 = private unnamed_addr constant [1 x i8] c"X"
@"$u3" = private constant [1 x i8] c"X"

With -O0 --data-sections

it emits:

        .text
        .def    "@feat.00";
        .scl    3;
        .type   0;
        .endef
        .globl  "@feat.00"
.set "@feat.00", 0
        .file   "Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba"
        .section        .rdata,"dr",one_only,__unnamed_1
__unnamed_1:                            // @0
        .byte   88

        .section        .rdata,"dr",one_only,$u3
$u3:                                    // @"$u3"
        .byte   88

while without --data-sections:

        .text
        .def    "@feat.00";
        .scl    3;
        .type   0;
        .endef
        .globl  "@feat.00"
.set "@feat.00", 0
        .file   "Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba"
        .section        .rdata,"dr"
.L__unnamed_1:                          // @0
        .byte   88

.L$u3:                                  // @"$u3"
        .byte   88

Note how the symbol gets properly hidden without datasections. The first version is causing conflicts on arm64 when I have multiple files with a @“$u3” or @0 symbol.
(Compiler Explorer)

Is there a workaronud for this? Is this a bug at all?

I’m not sure there’s a bug here, I think it depends on what it means to “properly hide” a symbol. In this case, LLVM is essentially treating private globals as if they were internal linkage globals. This is necessary because in order to create a comdat section for every global, it must have a symbol in the symbol table. A private label (one with the .L prefix) won’t work. You can try changing the assembly to add one, and observe the assembler errors.

However, nobody can link against these symbols, they are not external (no .globl directive). They persist in the object file and may be carried over into debug info, but they are mostly harmless aside from some ambiguity and perhaps information leakage.

See this example here showing how private acts like internal with data sections: Compiler Explorer

Thanks! I’ll dig some more tomorrow. I am getting duplicate symbols from lld (only on arm64/windows) for symbols like this. Seems i was looking in the wrong place.

Perhaps LLVM is wrong to use the “one_only” selection kind for private / internal comdat data sections. Maybe we should be using “any” instead. I would check what MSVC does for static global variables when data sections are enabled (/Gw).

I think I found the diff here between arm64, turns out I should have looked at functions not data:

; ModuleID = 'Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba'
source_filename = "Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba"
target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"
target triple = "arm64-pc-windows-msvc"

@a = private unnamed_addr constant ptr null
@b = constant ptr @a
@c = constant ptr @"$u3"
define private void @"$u3"() {
    BB1: 
      ret void
}
llc -O0 --data-sections arm64.ll -filetype=obj --function-sections```
008 00000000 SECT4  notype ()    External     | $u3

while the same one for x86-64:

; ModuleID = 'Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba'
source_filename = "Monitor-c5b6f68328f4c1f09d4ad70a8a88eaba"
target datalayout = "e-m:w-p:64:64-i32:32-i64:64-i128:128-n32:64-S128"
target triple = "x86_64-pc-windows-msvc"

@a = private unnamed_addr constant ptr null
@d = constant ptr @a
@e = constant ptr @"$u3"
define private void @"$u3"() {
    BB1: 
      ret void
}

llc -O0 --data-sections x8664.ll -filetype=obj --function-sections

008 00000000 SECT4  notype ()    Static       | $u3

The curious thing here that the .s files are fine, this only seems to happen when objdump’ing .o files?

Yeah, “External” seems wrong. I think you’ve definitely found a bug. :slight_smile:

I would compare with internal linkage, which presumably works, and follow the codepaths through MC to see where things go wrong.

That hint did help me. Internal linkage works. I’ll dig further to see if I can find where the cause of private not working, but at least we can move on! Thanks