[llvm-dev] [BUG Report] -dead_strip, strips prefix data unconditionally on macOS

Moritz Angermann via llvm-dev
2017-03-07T01:54:59Z

Hi Peter,

I’ve just experimented with this a bit:

Say we would end up with the following assembly:

  .section  __TEXT,__text
  .globl _main

  .long 1
_main:
  inc %eax
  ret

  .globl  _main.dsp
  .alt_entry      _main.dsp
_main.dsp = _main-4

  .subsections_via_symbols

(e.g. we inject the .alt_entry after the fact, pointing to the start of the
prefix data)

this will yield:

$ clang test.s -dead_strip
ld: warning: N_ALT_ENTRY bit set on first atom in section __TEXT/__text

And the prefix data will be stripped again.

E.g. what you end up getting is:

$ otool -vVtdj a.out
a.out:
_main:
0000000100000fb5        ff c0   incl    %eax
0000000100000fb7        c3      retq

instead of what we’d like to get:

otool -vVtdj a.out
a.out:
_main.dsp:
0000000100000fb1        01 00   addl    %eax, (%rax)
0000000100000fb3        00 00   addb    %al, (%rax)
_main:
0000000100000fb5        ff c0   incl    %eax
0000000100000fb7        c3      retq

.alt_entry’s are not dead_strip protected, and this makes sense I guess, as if
the alt_entry is never
actually called visibly from anywhere, it’s probably not needed. However there
is the .no_daed_strip
directive. Thus if we graft this slightly different:

  .section  __TEXT,__text
  .globl _main

  .long 1
_main:
  inc %eax
  ret

  .no_dead_strip  _main.dsp
  .alt_entry      _main.dsp
_main.dsp = _main-4

  .subsections_via_symbols

we still get a warning, but it won’t get stripped. At that point however, we
don’t need the .alt_entry
anymore (and can drop the warning).

Thus, I’d propose that for functions with prefix_data, a second symbol with
.no_dead_strip is emitted
for the prefix data entry point.

Cheers,
 Moritz


> On Mar 7, 2017, at 3:35 AM, Peter Collingbourne <peter at pcc.me.uk>
wrote:
>
> That is in theory what omitting the .subsections_via_symbols directive is
supposed to do, but in an experiment I ran a year or two ago I found that the
Mach-O linker was still dead stripping on symbol boundaries with this directive
omitted.
>
> In any case, a more precise approach has more recently (~a few months ago)
become possible. There is a relatively new asm directive called .altentry that,
as I understand it, tells the linker to disregard a given symbol as a section
boundary (LLVM already uses this for aliases pointing into the middle of a
global). So what you would do is to use .altentry on the function symbol, with
an internal symbol appearing before the prefix data to ensure that it is not
considered part of the body of the previous function.
>
> Peter
>
> On Mon, Mar 6, 2017 at 11:19 AM, James Y Knight via llvm-dev <llvm-dev
at lists.llvm.org> wrote:
> AFAIK, this cannot actually work on Apple platforms, because its object
file format (Mach-O) doesn't use sections to determine the ranges of
code/data to keep together, but instead _infers_ boundaries based on the range
between global symbols in the symbol table.
>
> So, the symbol pointing to the beginning of @main *necessarily* makes that
be a section boundary.
>
> I think the best that could be done in LLVM is to not emit the
".subsections_via_symbols" asm directive (effectively disabling dead
stripping on that object) if any prefix data exists. Currently it emits that
flag unconditionally for MachO.
>
> On Mon, Mar 6, 2017 at 4:40 AM, Moritz Angermann via llvm-dev <llvm-dev
at lists.llvm.org> wrote:
> Hi,
>
> I just came across a rather annoying behavior with llvm 3.9. Assuming the
following
> samle code in test.ll:
>
> ; Lets have some global int x = 4
> @x = global i32 10, align 4
> ; and two strings "p = %d\n" for the prefix data,
> ; as well as "x = %d\n" to print the (global) x value.
> @.str = private unnamed_addr constant [8 x i8] c"x = %d\0A\00",
align 1
> @.str2 = private unnamed_addr constant [8 x i8] c"p = %d\0A\00",
align 1
>
> ; declare printf, we'll use this later for printf style debugging.
> declare i32 @printf(i8*, ...)
>
> ; define a main function.
> define i32 @main() prefix i32 123 {
>   ; obtain a i32 pointer to the main function.
>   ; the prefix data is right before that pointer.
>   %main = bitcast i32 ()* @main to i32*
>
>   ; use the gep, to cmpute the start of the prefix data.
>   %prefix_ptr = getelementptr inbounds i32, i32* %main, i32 -1
>   ; and load it.
>   %prefix_val = load i32, i32* %prefix_ptr
>
>   ; print that value.
>   %ret = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8],
[8 x i8]* @.str2, i32 0, i32 0), i32 %prefix_val)
>
>   ; similarly let's do the same with the global x.
>   %1 = alloca i32, align 4
>   store i32 0, i32* %1, align 4
>   %2 = load i32, i32* @x, align 4
>   %3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([8 x i8], [8
x i8]* @.str, i32 0, i32 0), i32 %2)
>   ret i32 0
> }
>
> gives the following result (expected)
>
>    $ clang test.ll
>    $ ./a.out
>    p = 123
>    x = 10
>
> however, with -dead_strip on macOS, we see the following:
>
>    $ clang test.ll -dead_strip
>    $ ./a.out
>    p = 0
>    x = 10
>
> Thus I believe we are incorrectly stripping prefix data when linking with
-dead_strip on macOS.
>
> As I do not have a bugzilla account, and hence cannot post this as a proper
bug report.
>
> Cheers,
>  Moritz