public inbox for gcc-help@gcc.gnu.org
 help / color / mirror / Atom feed
* Symbol resolution differs when building with LTO compared to building without
@ 2020-06-16  2:56 Alice Wang
  2020-06-28 11:54 ` Kewen.Lin
  0 siblings, 1 reply; 4+ messages in thread
From: Alice Wang @ 2020-06-16  2:56 UTC (permalink / raw)
  To: gcc-help

Hi,

I've come across some unexpected behavior and I'd appreciate your input on
the issue. I have an example project below that reproduces the issue. In
short, when building the project without LTO, a function will resolve to my
expected version, but when building with LTO, the symbol is resolved to a
different definition that I do not want.

Specifically, when building without LTO (which results in the behavior I
want), the final binary will pull in the definition of `_write` from my
custom static library that is listed first in the library list. With LTO
enabled (the bad case), the final binary will pull in the definition of
`_write` from libnosys, which comes later in the list of static libraries.

The output of my test project is pasted below. `make` builds without LTO
and dumping the resulting binary shows that `_write` is my stub
implementation. `LTO=1 make` builds with LTO enabled, and dumping the
resulting binary shows the implementation comes from libnosys.

How can I enable LTO in my build while providing custom definitions for
library calls (like _write)?

~/D/lto_test ❯❯❯ make

Final binary:
00008ae0 <_write>:
    8ae0: 4610       mov r0, r2
    8ae2: 4770       bx lr

~/D/lto_test ❯❯❯ make clean
~/D/lto_test ❯❯❯ LTO=1 make

Final binary:
00008b50 <_write>:
    8b50: 4b02       ldr r3, [pc, #8] ; (8b5c <_write+0xc>)
    8b52: 2258       movs r2, #88 ; 0x58
    8b54: 601a       str r2, [r3, #0]
    8b56: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff
    8b5a: 4770       bx lr
    8b5c: 00018c84 .word 0x00018c84

------------------------------------------------

Toolchain version:
ARM 9-2020-q2-update (based on the 9 series)

Repro steps:
make
make clean
LTO=1 make

Repro project:

main.c
```
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    printf("Hello.\n");
    return 0;
}
```

write.c
```
#include  <unistd.h>

ssize_t  _write (int      fd,
                 void    *p_buf,
                 size_t   cnt)
{
    return cnt;
}
```

Makefile
Note: You'll have to change the path to point to your local installation of
the ARM 2020 Q2 toolchain.
```
TOOLCHAIN=<YOUR_PATH_TO_TOOLCHAIN>
LTO_PLUGIN=$(TOOLCHAIN)/lib/gcc/arm-none-eabi/9.3.1/liblto_plugin.so

CC=$(TOOLCHAIN)/bin/arm-none-eabi-gcc
AR=$(TOOLCHAIN)/bin/arm-none-eabi-ar
DUMP=$(TOOLCHAIN)/bin/arm-none-eabi-objdump
NM=$(TOOLCHAIN)/bin/arm-none-eabi-gcc-nm

FLAGS=-g -Os -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections
--specs=nano.specs --specs=nosys.specs
ifeq ($(LTO),1)
FLAGS+=-flto -ffat-lto-objects
endif


.PHONY: dump
dump: main.elf _my_write.o
@echo "\n======================== Dump ========================"
@$(NM) -nS _my_write.o
@echo "\n==================== Final binary ===================="
@$(DUMP) -d $< | grep "<_write>:" -A 6
@echo "\n====================== _my_write.o ======================"
@$(DUMP) -d $(word 2,$^) | grep "<_write>:" -A 6
@echo "\n======================= libnosys ========================"
@$(DUMP) -d $(TOOLCHAIN)/arm-none-eabi/lib/thumb/v7e-m/nofp/libnosys.a |
grep "<_write>:" -A 6

main.elf: main.a _my_write.a
@$(CC) $(FLAGS)  -fuse-linker-plugin -fno-common -Wl,-Map=output.map
-Wl,--gc-sections -o $@ -Wl,--start-group $^ -lc -Wl,--end-group

%.a: %.o
@$(AR) --plugin $(LTO_PLUGIN) -rcs $@ $<

_my_write.o: ./write.c Makefile
@$(CC) $(FLAGS) -c $< -o $@

main.o: main.c Makefile
@$(CC) $(FLAGS) -c $< -o $@

.PHONY: clean
clean:
@rm -rf _write.a main.a main.elf *.res *.out *.o *.s *.map *.dump *.a
```

Once again, any insight would be greatly appreciated. My goal is to enable
LTO in my project while being able to provide custom definitions of
specific standard library calls.

Thanks in advance.

From,
AW

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Symbol resolution differs when building with LTO compared to building without
  2020-06-16  2:56 Symbol resolution differs when building with LTO compared to building without Alice Wang
@ 2020-06-28 11:54 ` Kewen.Lin
  2020-07-09 20:00   ` Alice Wang
  0 siblings, 1 reply; 4+ messages in thread
From: Kewen.Lin @ 2020-06-28 11:54 UTC (permalink / raw)
  To: Alice Wang, gcc-help

on 2020/6/16 上午10:56, Alice Wang via Gcc-help wrote:
> Hi,
> 
> I've come across some unexpected behavior and I'd appreciate your input on
> the issue. I have an example project below that reproduces the issue. In
> short, when building the project without LTO, a function will resolve to my
> expected version, but when building with LTO, the symbol is resolved to a
> different definition that I do not want.
> 
> Specifically, when building without LTO (which results in the behavior I
> want), the final binary will pull in the definition of `_write` from my
> custom static library that is listed first in the library list. With LTO
> enabled (the bad case), the final binary will pull in the definition of
> `_write` from libnosys, which comes later in the list of static libraries.
> 
> The output of my test project is pasted below. `make` builds without LTO
> and dumping the resulting binary shows that `_write` is my stub
> implementation. `LTO=1 make` builds with LTO enabled, and dumping the
> resulting binary shows the implementation comes from libnosys.
> 
> How can I enable LTO in my build while providing custom definitions for
> library calls (like _write)?
> 
> ~/D/lto_test ❯❯❯ make
> 
> Final binary:
> 00008ae0 <_write>:
>     8ae0: 4610       mov r0, r2
>     8ae2: 4770       bx lr
> 
> ~/D/lto_test ❯❯❯ make clean
> ~/D/lto_test ❯❯❯ LTO=1 make
> 
> Final binary:
> 00008b50 <_write>:
>     8b50: 4b02       ldr r3, [pc, #8] ; (8b5c <_write+0xc>)
>     8b52: 2258       movs r2, #88 ; 0x58
>     8b54: 601a       str r2, [r3, #0]
>     8b56: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff
>     8b5a: 4770       bx lr
>     8b5c: 00018c84 .word 0x00018c84
> 
> ------------------------------------------------
> 
> Toolchain version:
> ARM 9-2020-q2-update (based on the 9 series)
> 
> Repro steps:
> make
> make clean
> LTO=1 make
> 
> Repro project:
> 
> main.c
> ```
> #include <stdint.h>
> #include <stdlib.h>
> #include <stdio.h>
> #include <string.h>
> 
> int main(int argc, char *argv[]) {
>     printf("Hello.\n");
>     return 0;
> }
> ```
> 
> write.c
> ```
> #include  <unistd.h>
> 
> ssize_t  _write (int      fd,
>                  void    *p_buf,
>                  size_t   cnt)
> {
>     return cnt;
> }
> ```
> 
> Makefile
> Note: You'll have to change the path to point to your local installation of
> the ARM 2020 Q2 toolchain.
> ```
> TOOLCHAIN=<YOUR_PATH_TO_TOOLCHAIN>
> LTO_PLUGIN=$(TOOLCHAIN)/lib/gcc/arm-none-eabi/9.3.1/liblto_plugin.so
> 
> CC=$(TOOLCHAIN)/bin/arm-none-eabi-gcc
> AR=$(TOOLCHAIN)/bin/arm-none-eabi-ar
> DUMP=$(TOOLCHAIN)/bin/arm-none-eabi-objdump
> NM=$(TOOLCHAIN)/bin/arm-none-eabi-gcc-nm
> 
> FLAGS=-g -Os -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections
> --specs=nano.specs --specs=nosys.specs
> ifeq ($(LTO),1)
> FLAGS+=-flto -ffat-lto-objects
> endif
> 
> 
> .PHONY: dump
> dump: main.elf _my_write.o
> @echo "\n======================== Dump ========================"
> @$(NM) -nS _my_write.o
> @echo "\n==================== Final binary ===================="
> @$(DUMP) -d $< | grep "<_write>:" -A 6
> @echo "\n====================== _my_write.o ======================"
> @$(DUMP) -d $(word 2,$^) | grep "<_write>:" -A 6
> @echo "\n======================= libnosys ========================"
> @$(DUMP) -d $(TOOLCHAIN)/arm-none-eabi/lib/thumb/v7e-m/nofp/libnosys.a |
> grep "<_write>:" -A 6
> 
> main.elf: main.a _my_write.a
> @$(CC) $(FLAGS)  -fuse-linker-plugin -fno-common -Wl,-Map=output.map
> -Wl,--gc-sections -o $@ -Wl,--start-group $^ -lc -Wl,--end-group
> 
> %.a: %.o
> @$(AR) --plugin $(LTO_PLUGIN) -rcs $@ $<
> 
> _my_write.o: ./write.c Makefile
> @$(CC) $(FLAGS) -c $< -o $@
> 
> main.o: main.c Makefile
> @$(CC) $(FLAGS) -c $< -o $@
> 
> .PHONY: clean
> clean:
> @rm -rf _write.a main.a main.elf *.res *.out *.o *.s *.map *.dump *.a
> ```
> 
> Once again, any insight would be greatly appreciated. My goal is to enable
> LTO in my project while being able to provide custom definitions of
> specific standard library calls.
> 
> Thanks in advance.
> 
> From,
> AW
> 

Hi Alice,

This symptom made me recall one PR https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91287

Sorry that I don't have the environment for reproduction locally.

Do you mind to try each of below:
  1) Don't use linker-plugin, use -fno-use-linker-plugin explicitly.
  2) Use shared library instead of static library for _write library.
  3) -Wl,--whole-archive -l<your_static_write_library> -Wl,--no-whole-archive
  
HTH.

BR,
Kewen

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Symbol resolution differs when building with LTO compared to building without
  2020-06-28 11:54 ` Kewen.Lin
@ 2020-07-09 20:00   ` Alice Wang
  2020-07-14 10:43     ` Kewen.Lin
  0 siblings, 1 reply; 4+ messages in thread
From: Alice Wang @ 2020-07-09 20:00 UTC (permalink / raw)
  To: Kewen.Lin; +Cc: gcc-help

Hi Kewen,

The ticket you linked does sound similar. And thanks for your suggestions.
I tried them out and summarized the results below:

1) Don't use linker-plugin, use -fno-use-linker-plugin explicitly.

The build finishes and outputs the result I expect. My custom
implementation of _write is pulled into the final binary. But wouldn't
-fno-use-linker-plugin effectively disable LTO in the sense that my project
won't be able to take advantage of more aggressive
interprocedural optimizations?

2) Use shared library instead of static library for _write library.

I can't use a shared library in my case since I'm compiling for an embedded
target without any sort of dynamic linker.

3) -Wl,--whole-archive -l<your_static_write_library> -Wl,--no-whole-archive

I changed the build command to the following:
 @$(CC) $(FLAGS)  -fuse-linker-plugin -fno-common -Wl,-Map=output.map
-Wl,--gc-sections -o $@ -Wl,--start-group main.a -lc -Wl,--end-group
-Wl,--whole-archive _my_write.a -Wl,--no-whole-archive

Result:
*```*
> LTO=1 make
.cmake/install/GNUARM-9.2.1-Darwin/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/bin/ld:
/Users/aw/.cmake/install/GNUARM-9.2.1-Darwin/bin/../lib/gcc/arm-none-eabi/9.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m/nofp/libc_nano.a(lib_a-writer.o):
in function `_write_r':
writer.c:(.text._write_r+0x10): undefined reference to `_write'
collect2: error: ld returned 1 exit status
make: *** [main.elf] Error 1
```
Can't seem to link when using whole archives.

If you have any more suggestions, it would be much appreciated!

From,
Aw

On Sun, Jun 28, 2020 at 4:54 AM Kewen.Lin <linkw@linux.ibm.com> wrote:

> on 2020/6/16 上午10:56, Alice Wang via Gcc-help wrote:
> > Hi,
> >
> > I've come across some unexpected behavior and I'd appreciate your input
> on
> > the issue. I have an example project below that reproduces the issue. In
> > short, when building the project without LTO, a function will resolve to
> my
> > expected version, but when building with LTO, the symbol is resolved to a
> > different definition that I do not want.
> >
> > Specifically, when building without LTO (which results in the behavior I
> > want), the final binary will pull in the definition of `_write` from my
> > custom static library that is listed first in the library list. With LTO
> > enabled (the bad case), the final binary will pull in the definition of
> > `_write` from libnosys, which comes later in the list of static
> libraries.
> >
> > The output of my test project is pasted below. `make` builds without LTO
> > and dumping the resulting binary shows that `_write` is my stub
> > implementation. `LTO=1 make` builds with LTO enabled, and dumping the
> > resulting binary shows the implementation comes from libnosys.
> >
> > How can I enable LTO in my build while providing custom definitions for
> > library calls (like _write)?
> >
> > ~/D/lto_test ❯❯❯ make
> >
> > Final binary:
> > 00008ae0 <_write>:
> >     8ae0: 4610       mov r0, r2
> >     8ae2: 4770       bx lr
> >
> > ~/D/lto_test ❯❯❯ make clean
> > ~/D/lto_test ❯❯❯ LTO=1 make
> >
> > Final binary:
> > 00008b50 <_write>:
> >     8b50: 4b02       ldr r3, [pc, #8] ; (8b5c <_write+0xc>)
> >     8b52: 2258       movs r2, #88 ; 0x58
> >     8b54: 601a       str r2, [r3, #0]
> >     8b56: f04f 30ff mov.w r0, #4294967295 ; 0xffffffff
> >     8b5a: 4770       bx lr
> >     8b5c: 00018c84 .word 0x00018c84
> >
> > ------------------------------------------------
> >
> > Toolchain version:
> > ARM 9-2020-q2-update (based on the 9 series)
> >
> > Repro steps:
> > make
> > make clean
> > LTO=1 make
> >
> > Repro project:
> >
> > main.c
> > ```
> > #include <stdint.h>
> > #include <stdlib.h>
> > #include <stdio.h>
> > #include <string.h>
> >
> > int main(int argc, char *argv[]) {
> >     printf("Hello.\n");
> >     return 0;
> > }
> > ```
> >
> > write.c
> > ```
> > #include  <unistd.h>
> >
> > ssize_t  _write (int      fd,
> >                  void    *p_buf,
> >                  size_t   cnt)
> > {
> >     return cnt;
> > }
> > ```
> >
> > Makefile
> > Note: You'll have to change the path to point to your local installation
> of
> > the ARM 2020 Q2 toolchain.
> > ```
> > TOOLCHAIN=<YOUR_PATH_TO_TOOLCHAIN>
> > LTO_PLUGIN=$(TOOLCHAIN)/lib/gcc/arm-none-eabi/9.3.1/liblto_plugin.so
> >
> > CC=$(TOOLCHAIN)/bin/arm-none-eabi-gcc
> > AR=$(TOOLCHAIN)/bin/arm-none-eabi-ar
> > DUMP=$(TOOLCHAIN)/bin/arm-none-eabi-objdump
> > NM=$(TOOLCHAIN)/bin/arm-none-eabi-gcc-nm
> >
> > FLAGS=-g -Os -mthumb -mcpu=cortex-m4 -ffunction-sections -fdata-sections
> > --specs=nano.specs --specs=nosys.specs
> > ifeq ($(LTO),1)
> > FLAGS+=-flto -ffat-lto-objects
> > endif
> >
> >
> > .PHONY: dump
> > dump: main.elf _my_write.o
> > @echo "\n======================== Dump ========================"
> > @$(NM) -nS _my_write.o
> > @echo "\n==================== Final binary ===================="
> > @$(DUMP) -d $< | grep "<_write>:" -A 6
> > @echo "\n====================== _my_write.o ======================"
> > @$(DUMP) -d $(word 2,$^) | grep "<_write>:" -A 6
> > @echo "\n======================= libnosys ========================"
> > @$(DUMP) -d $(TOOLCHAIN)/arm-none-eabi/lib/thumb/v7e-m/nofp/libnosys.a |
> > grep "<_write>:" -A 6
> >
> > main.elf: main.a _my_write.a
> > @$(CC) $(FLAGS)  -fuse-linker-plugin -fno-common -Wl,-Map=output.map
> > -Wl,--gc-sections -o $@ -Wl,--start-group $^ -lc -Wl,--end-group
> >
> > %.a: %.o
> > @$(AR) --plugin $(LTO_PLUGIN) -rcs $@ $<
> >
> > _my_write.o: ./write.c Makefile
> > @$(CC) $(FLAGS) -c $< -o $@
> >
> > main.o: main.c Makefile
> > @$(CC) $(FLAGS) -c $< -o $@
> >
> > .PHONY: clean
> > clean:
> > @rm -rf _write.a main.a main.elf *.res *.out *.o *.s *.map *.dump *.a
> > ```
> >
> > Once again, any insight would be greatly appreciated. My goal is to
> enable
> > LTO in my project while being able to provide custom definitions of
> > specific standard library calls.
> >
> > Thanks in advance.
> >
> > From,
> > AW
> >
>
> Hi Alice,
>
> This symptom made me recall one PR
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91287
>
> Sorry that I don't have the environment for reproduction locally.
>
> Do you mind to try each of below:
>   1) Don't use linker-plugin, use -fno-use-linker-plugin explicitly.
>   2) Use shared library instead of static library for _write library.
>   3) -Wl,--whole-archive -l<your_static_write_library>
> -Wl,--no-whole-archive
>
> HTH.
>
> BR,
> Kewen
>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: Symbol resolution differs when building with LTO compared to building without
  2020-07-09 20:00   ` Alice Wang
@ 2020-07-14 10:43     ` Kewen.Lin
  0 siblings, 0 replies; 4+ messages in thread
From: Kewen.Lin @ 2020-07-14 10:43 UTC (permalink / raw)
  To: Alice Wang; +Cc: gcc-help

Hi Alice,

on 2020/7/10 上午4:00, Alice Wang wrote:
> Hi Kewen,
> 
> The ticket you linked does sound similar. And thanks for your suggestions. I tried them out and summarized the results below:
> 
> 1) Don't use linker-plugin, use -fno-use-linker-plugin explicitly.
> 
> The build finishes and outputs the result I expect. My custom implementation of _write is pulled into the final binary. But wouldn't -fno-use-linker-plugin effectively disable LTO in the sense that my project won't be able to take advantage of more aggressive interprocedural optimizations? 
> 

Yes, I just noticed your write.c has its own static library (archive), you would need plugin
to extract these files in archive.  Do you mind to try with option "-u _write" in the linking
command to explicitly claim symbol _write is required.  Hope it can pull your expected object
file from the archive.

> 2) Use shared library instead of static library for _write library.
> 
> I can't use a shared library in my case since I'm compiling for an embedded target without any sort of dynamic linker. 
> 

OK.

> 3) -Wl,--whole-archive -l<your_static_write_library> -Wl,--no-whole-archive
> 
> I changed the build command to the following:
>  @$(CC) $(FLAGS)  -fuse-linker-plugin -fno-common -Wl,-Map=output.map -Wl,--gc-sections -o $@ -Wl,--start-group main.a -lc -Wl,--end-group -Wl,--whole-archive _my_write.a -Wl,--no-whole-archive
> 

As you mentioned your project is for embedded env, then I guess the size is critical and
this option isn't acceptable for you since it could pull in some useless objects from
the archive.  So it looks you can just ignore it.  :)


BR,
Kewen

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2020-07-14 10:43 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-16  2:56 Symbol resolution differs when building with LTO compared to building without Alice Wang
2020-06-28 11:54 ` Kewen.Lin
2020-07-09 20:00   ` Alice Wang
2020-07-14 10:43     ` Kewen.Lin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).