• [OT] Linking against a static library

    From pozz@pozzugno@gmail.com to comp.lang.c on Thu Aug 28 18:12:52 2025
    From Newsgroup: comp.lang.c

    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files except
    main.o, and the final executable linking together main.o and mylib.a.

    I want to remove from the final exe everything present in mylib.a that
    is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are defined
    in mod1.c. main() is the only function in main.c and only foo1() is
    called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a
    objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in WSL doesn't.

    Why?
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From scott@scott@slp53.sl.home (Scott Lurndal) to comp.lang.c on Thu Aug 28 17:08:42 2025
    From Newsgroup: comp.lang.c

    pozz <pozzugno@gmail.com> writes:
    I don't think this is a pure C programming question, but it's related.


    It is more appropriate to the windows programming groups or whomever
    built the compilers you're using.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Thu Aug 28 17:51:17 2025
    From Newsgroup: comp.lang.c

    pozz <pozzugno@gmail.com> wrote:
    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files except main.o, and the final executable linking together main.o and mylib.a.

    I want to remove from the final exe everything present in mylib.a that
    is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are defined
    in mod1.c. main() is the only function in main.c and only foo1() is
    called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in WSL doesn't.

    Why?

    Support for '-ffunction-sections' and '-fdata-sections' is target
    platform dependent (and version dependent). Also there may be
    platform specific quirks. If your toolchain does not properly
    support them, then you need to use old method, that is define each
    function in a separate file.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.c on Fri Aug 29 00:45:15 2025
    From Newsgroup: comp.lang.c

    On 2025-08-28, Waldek Hebisch <antispam@fricas.org> wrote:
    pozz <pozzugno@gmail.com> wrote:
    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files except
    main.o, and the final executable linking together main.o and mylib.a.

    I want to remove from the final exe everything present in mylib.a that
    is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are defined
    in mod1.c. main() is the only function in main.c and only foo1() is
    called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a
    objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in WSL
    doesn't.

    Why?

    Support for '-ffunction-sections' and '-fdata-sections' is target
    platform dependent (and version dependent). Also there may be
    platform specific quirks. If your toolchain does not properly
    support them, then you need to use old method, that is define each
    function in a separate file.

    I think libgcc still does this?

    You can keep the functions in the same physical files but you can
    compile it multiple times, using #if or #ifdef to reveal different
    function definitions in different compiler passes, and making sure
    that the object file name is different for each function, of course.

    Tangential anecdote vaguely related to this topic: almost three decades
    ago, there was a driver in the Linux kernel whose source file was set up
    to be compiled N times for N instances of the device, using the
    preprocessor to generate certain constants and whatnot to distinguish
    the devices. I seem to remember it was the "sbpcd" driver: for the
    Sound Blaster card driving the Panasonic CD-ROM interface.
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Aug 29 11:05:28 2025
    From Newsgroup: comp.lang.c

    On 28/08/2025 18:12, pozz wrote:
    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files except main.o, and the final executable linking together main.o and mylib.a.


    My first question is "Why?" Why not simply link all the object files together?

    I want to remove from the final exe everything present in mylib.a that
    is not used in main.o.


    My second question is "Why?" When you are working on embedded targets,
    saving space like this in the executable is often a very good idea. On
    a PC, it is rarely worth the effort.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are defined
    in mod1.c. main() is the only function in main.c and only foo1() is
    called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in WSL doesn't.

    Why?

    Why are you using "-ffunction-sections" and "-fdata-sections" ? They
    can be helpful in some ways for omitting code and data that is defined
    in the code, but not actually used in the executable. But they can also
    make linking slower, and "-fdata-sections" can reduce optimisations (especially if you have "-fcommon", which was the default in older gcc).

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Fri Aug 29 12:46:42 2025
    From Newsgroup: comp.lang.c

    On Thu, 28 Aug 2025 18:12:52 +0200
    pozz <pozzugno@gmail.com> wrote:

    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files
    except main.o, and the final executable linking together main.o and
    mylib.a.

    I want to remove from the final exe everything present in mylib.a
    that is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in
    WSL doesn't.

    Why?

    Gnu ld documents say that support for "garbage collection" on COFF/PE
    targets works, but considered experimental. I would suppose that it
    means that it's not yet a part of their regression tests suit.
    So it's possible that you found a regression that developers are
    not aware about. Probably it is worth reporting.

    BTW, it seems that only unused code sections are not discarded. Unused
    data sections are discarded just fine.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From pozz@pozzugno@gmail.com to comp.lang.c on Fri Aug 29 12:39:05 2025
    From Newsgroup: comp.lang.c

    Il 29/08/2025 11:05, David Brown ha scritto:
    On 28/08/2025 18:12, pozz wrote:
    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files except
    main.o, and the final executable linking together main.o and mylib.a.

    My first question is "Why?"  Why not simply link all the object files together?

    Because I want to start creating tests on my projects and one simple
    approach is what I have described. All is in a static library (a
    collection of object files), except main.o where is only main().

    If I want to create test1.c (with its main), I have to only link test1.o
    with library.


    I want to remove from the final exe everything present in mylib.a that
    is not used in main.o.

    My second question is "Why?"  When you are working on embedded targets, saving space like this in the executable is often a very good idea.  On
    a PC, it is rarely worth the effort.

    This question was born because I found another problem.

    I have a library distributed in source codes. One function in one source
    code needs to access a global array that *must* be defined somewhere in
    the project.

    As I wrote, I'm creating some tests. Not all the tests use that
    function, so I thought it would be safe to not have the definition of
    the global array, but this isn't true in my project and building system.
    The linker complains because it doesn't find the global array.

    I expected the unused function, with its references, would have been
    removed by the linker.


    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a
    objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in
    WSL doesn't.

    Why?

    Why are you using "-ffunction-sections" and "-fdata-sections" ?

    Because each function is in its section and can be removed if unused by
    the garbage collector of the linker (at least, so I understood).


    They
    can be helpful in some ways for omitting code and data that is defined
    in the code, but not actually used in the executable.  But they can also make linking slower, and "-fdata-sections" can reduce optimisations (especially if you have "-fcommon", which was the default in older gcc).


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Fri Aug 29 15:04:48 2025
    From Newsgroup: comp.lang.c

    On 29/08/2025 12:39, pozz wrote:
    Il 29/08/2025 11:05, David Brown ha scritto:
    On 28/08/2025 18:12, pozz wrote:
    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files
    except main.o, and the final executable linking together main.o and
    mylib.a.

    My first question is "Why?"  Why not simply link all the object files
    together?

    Because I want to start creating tests on my projects and one simple approach is what I have described. All is in a static library (a
    collection of object files), except main.o where is only main().

    If I want to create test1.c (with its main), I have to only link test1.o with library.


    The only "benefit" you get from using a library in this situation is
    that you might conceivably save a couple of lines in your makefile, and
    if you are using spinning rust drives and a PC from the 1990's, you
    might save a second or two from the build. It is not worth the bother.


    I want to remove from the final exe everything present in mylib.a
    that is not used in main.o.

    My second question is "Why?"  When you are working on embedded
    targets, saving space like this in the executable is often a very good
    idea.  On a PC, it is rarely worth the effort.

    This question was born because I found another problem.

    I have a library distributed in source codes. One function in one source code needs to access a global array that *must* be defined somewhere in
    the project.

    Okay.


    As I wrote, I'm creating some tests. Not all the tests use that
    function, so I thought it would be safe to not have the definition of
    the global array, but this isn't true in my project and building system.
    The linker complains because it doesn't find the global array.

    I expected the unused function, with its references, would have been
    removed by the linker.

    The "--gc-sections" gnu linker option works for elf files, but is "experimental" for coff/pe. Maybe it simply doesn't work as effectively
    for mingw, which will be generating Windows-style coff/pe binaries,
    while it works for WSL which uses Linux-style elf binaries.


    But while I appreciate that you expected linker garbage collection to
    work here, you still haven't answered why you feel it is important. (If
    you are just trying to understand why it doesn't work, that's fine by me.)



    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a
    objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in
    WSL doesn't.

    Why?

    Why are you using "-ffunction-sections" and "-fdata-sections" ?

    Because each function is in its section and can be removed if unused by
    the garbage collector of the linker (at least, so I understood).


    They are potentially useful if you have a significant amount of extra
    code and/or data that is in your source code and not wanted in the final binary, /and/ that it is important for the binary to be as small as
    reasonably possible.

    As I understand it, you have an embedded program with lots of source
    code, and you want to test different parts of it by compiling with
    different small "main" functions on a PC. I am not sure if you said the
    main program was for an embedded system, or if I am assuming that
    because you are one of the few people keeping comp.arch.embedded alive
    by starting new threads there :-)

    For the main program, function and data sections are probably not needed because you the source code that you build for the project is needed for
    the program - if there was a lot that you didn't need, it wouldn't be in
    the project build.

    For the test programs, function and data sections are not needed because
    the size of the test binaries on the PC is irrelevant.

    Again - if your questions are from curiosity as to why things are not
    working as you expected, I fully appreciate that. If your questions are because you think there is a significant advantage in using static
    libraries and section garbage collection in your build process, I
    believe it is unlikely to be beneficial in reality - so it does not
    matter if they don't work.

    In the end, however, my guess is just that the limited coff/pe format
    used by Windows binaries is the issue.


    They can be helpful in some ways for omitting code and data that is
    defined in the code, but not actually used in the executable.  But
    they can also make linking slower, and "-fdata-sections" can reduce
    optimisations (especially if you have "-fcommon", which was the
    default in older gcc).



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Jack@Jack@invalid.invalid to comp.lang.c, comp.lang.c++ on Sun Aug 31 18:16:14 2025
    From Newsgroup: comp.lang.c

    On 28/08/2025 17:12, pozz wrote:
    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files except main.o, and the final executable linking together main.o and mylib.a.

    I want to remove from the final exe everything present in mylib.a that
    is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are defined
    in mod1.c. main() is the only function in main.c and only foo1() is
    called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in WSL doesn't.

    Why?


    I am not sure whether you are creating a DLL file for your library. I
    ask this because you mentioned "final exe" which implies Windows
    executable file that uses DLL file.

    Step 1 is to create and compile your library source code:

    // mycode.c
    #include <stdio.h>

    // Exported function
    __declspec(dllexport) void hello() {
    printf("Hello from DLL!\n");
    }


    Step 2: compile it like so:

    gcc -shared -o mydll.dll mycode.c


    Step 3: create a header file:
    // mycode.h
    #ifndef MYCODE_H
    #define MYCODE_H

    __declspec(dllimport) void hello();

    #endif

    Step 4: Create an import library for linking:

    gcc -c mycode.c
    gcc -shared -o mydll.dll mycode.o -Wl,--out-implib,libmydll.a


    Step 5: Create a client to use your DLL

    // main.c
    #include "mycode.h"

    int main() {
    hello();
    return 0;
    }


    Step 6: Compile client to create main.exe file:

    gcc main.c -L. -lmydll -o main.exe


    These are all commands in few lines:

    # Compile and create DLL
    gcc -c mycode.c
    gcc -shared -o mydll.dll mycode.o -Wl,--out-implib,libmydll.a

    # Compile and link client
    gcc main.c -L. -lmydll -o main.exe

    This should work but is a streamlined version for this discussion.

    Good luck.




    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From pozz@pozzugno@gmail.com to comp.lang.c on Mon Sep 1 11:28:15 2025
    From Newsgroup: comp.lang.c

    Il 29/08/2025 15:04, David Brown ha scritto:
    On 29/08/2025 12:39, pozz wrote:
    Il 29/08/2025 11:05, David Brown ha scritto:
    On 28/08/2025 18:12, pozz wrote:
    I don't think this is a pure C programming question, but it's related. >>>>
    I'm building a static library mylib.a with all the object files
    except main.o, and the final executable linking together main.o and
    mylib.a.

    My first question is "Why?"  Why not simply link all the object files
    together?

    Because I want to start creating tests on my projects and one simple
    approach is what I have described. All is in a static library (a
    collection of object files), except main.o where is only main().

    If I want to create test1.c (with its main), I have to only link
    test1.o with library.


    The only "benefit" you get from using a library in this situation is
    that you might conceivably save a couple of lines in your makefile,

    Could you explain?

    Actually I'm using cmake and I write something similar to this for each
    test:

    add_executable(testA tests/testA.c)
    target_link_libraries(testA PRIVATE ${PRJLIB})
    target_link_options(testA PRIVATE -Wl,--gc-sections) # doesn't work

    Without the library I need to explictly link testA.c with the exact
    source files required for the test. I should take care of compilation
    options, because they should be the same for the main executable and the
    test executable (otherwise I could test a different thing). Linking
    against the library helps on this point, because you are sure your
    testing exactly the production binary.


    and
    if you are using spinning rust drives and a PC from the 1990's, you
    might save a second or two from the build.  It is not worth the bother.

    No, it's not for that I'm using static library approach for testing.


    I want to remove from the final exe everything present in mylib.a
    that is not used in main.o.

    My second question is "Why?"  When you are working on embedded
    targets, saving space like this in the executable is often a very
    good idea.  On a PC, it is rarely worth the effort.

    This question was born because I found another problem.

    I have a library distributed in source codes. One function in one
    source code needs to access a global array that *must* be defined
    somewhere in the project.

    Okay.


    As I wrote, I'm creating some tests. Not all the tests use that
    function, so I thought it would be safe to not have the definition of
    the global array, but this isn't true in my project and building
    system. The linker complains because it doesn't find the global array.

    I expected the unused function, with its references, would have been
    removed by the linker.

    The "--gc-sections" gnu linker option works for elf files, but is "experimental" for coff/pe.  Maybe it simply doesn't work as effectively for mingw, which will be generating Windows-style coff/pe binaries,
    while it works for WSL which uses Linux-style elf binaries.

    Ok, I expected it was a very simple task for a linker. Don't put an
    unused section in the final binary.


    But while I appreciate that you expected linker garbage collection to
    work here, you still haven't answered why you feel it is important.  (If you are just trying to understand why it doesn't work, that's fine by me.)

    I explained below. Anyway I know of countermeasures to solve the
    specific problem, but my original question is only to understand what
    was happening and if I was wrong in something.

    Again, I thought the linker garbace collector was a simple task to do.


    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a >>>> objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in
    WSL doesn't.

    Why?

    Why are you using "-ffunction-sections" and "-fdata-sections" ?

    Because each function is in its section and can be removed if unused
    by the garbage collector of the linker (at least, so I understood).

    They are potentially useful if you have a significant amount of extra
    code and/or data that is in your source code and not wanted in the final binary, /and/ that it is important for the binary to be as small as reasonably possible.

    Indeed they are used in embedded world, where the program memory is limited.


    As I understand it, you have an embedded program with lots of source
    code, and you want to test different parts of it by compiling with
    different small "main" functions on a PC.  I am not sure if you said the main program was for an embedded system, or if I am assuming that
    because you are one of the few people keeping comp.arch.embedded alive
    by starting new threads there :-)

    You have some relationship with Sherlock Homes :-)

    Yes, you described my situation very well. I have the principal main for production exe (embedded target) and a few test mains.(native for dev machine).


    For the main program, function and data sections are probably not needed because you the source code that you build for the project is needed for
    the program - if there was a lot that you didn't need, it wouldn't be in
    the project build.

    Indeed "-ffunction-sections" and "-fdata-sections" aren't important for
    main target main.c.


    For the test programs, function and data sections are not needed because
    the size of the test binaries on the PC is irrelevant.

    Of course, except when there are some functions that I'm not testing and
    that need some references that are defined in the main main(), but not
    in the test main().


    Again - if your questions are from curiosity as to why things are not working as you expected, I fully appreciate that.

    Yes, mainly my question was for curiosity. I already fixed my test by
    defining unreference data in the test main, even if it isn't really
    needed in the test.


    If your questions are
    because you think there is a significant advantage in using static
    libraries and section garbage collection in your build process, I
    believe it is unlikely to be beneficial in reality - so it does not
    matter if they don't work.

    I think now is clear. I needed linker garbage collector to produce test
    binary just to fix the undeclared error.
    Regarding the benefecial of building a static library to link with main
    main.c and test main.c, I think it's a good approach yet.


    In the end, however, my guess is just that the limited coff/pe format
    used by Windows binaries is the issue.

    Ok.



    They can be helpful in some ways for omitting code and data that is
    defined in the code, but not actually used in the executable.  But
    they can also make linking slower, and "-fdata-sections" can reduce
    optimisations (especially if you have "-fcommon", which was the
    default in older gcc).

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Mon Sep 1 22:52:00 2025
    From Newsgroup: comp.lang.c

    On Mon, 1 Sep 2025 11:28:15 +0200
    pozz <pozzugno@gmail.com> wrote:



    In the end, however, my guess is just that the limited coff/pe
    format used by Windows binaries is the issue.

    Ok.


    I find David Brown's explanation extremely unlikely.
    Microsoft's linker also generates COFF/PE format and it never had
    problems with linking in only the necessary stuff. They don't call it by
    fancy half-misleading names like 'garbage collection', in their world
    it is simply 'link' - a default "Release' policy since forever.

    If right now it does not work with Gnu ld then it happens most likely
    because COFF/PE is of low priority for Gnu ld devs rather than because
    COFF/PE is "limited".

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Tue Sep 2 02:02:20 2025
    From Newsgroup: comp.lang.c

    On Thu, 28 Aug 2025 18:12:52 +0200
    pozz <pozzugno@gmail.com> wrote:

    I don't think this is a pure C programming question, but it's related.

    I'm building a static library mylib.a with all the object files
    except main.o, and the final executable linking together main.o and
    mylib.a.

    I want to remove from the final exe everything present in mylib.a
    that is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in
    WSL doesn't.

    Why?

    I investigated it a little more and found two things:
    1. The problem caused by gcc compiler rather than by gnu ld
    linker. If you link MSVC-generated object files with gnu ld then
    "garbage collection" works as expected.
    2. The problem is related to gcc implementation of SEH.
    Compile to asm, edit asm removing SEH directives then assemble to .o
    and it works as expected.

    Now in order to continue from here I'd have to know much more
    about SEH than I actually do. So, I stopped.



    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Tue Sep 2 02:16:12 2025
    From Newsgroup: comp.lang.c

    On Tue, 2 Sep 2025 02:02:20 +0300
    Michael S <already5chosen@yahoo.com> wrote:

    On Thu, 28 Aug 2025 18:12:52 +0200
    pozz <pozzugno@gmail.com> wrote:

    I don't think this is a pure C programming question, but it's
    related.

    I'm building a static library mylib.a with all the object files
    except main.o, and the final executable linking together main.o and mylib.a.

    I want to remove from the final exe everything present in mylib.a
    that is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o
    mylib.a objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in
    WSL doesn't.

    Why?

    I investigated it a little more and found two things:
    1. The problem caused by gcc compiler rather than by gnu ld
    linker. If you link MSVC-generated object files with gnu ld then
    "garbage collection" works as expected.
    2. The problem is related to gcc implementation of SEH.
    Compile to asm, edit asm removing SEH directives then assemble to .o
    and it works as expected.

    Now in order to continue from here I'd have to know much more
    about SEH than I actually do. So, I stopped.





    I still don't know much about SEH, but now, due to Stack Overflow, I
    know how to get rid of it.
    Just add -fno-asynchronous-unwind-tables to your compilation options
    and suddenly 'garbage collection' works.
    Of course, it helps only if you don't care about SEH. But, considering
    that what you are doing are mere unit tests, I don't see why would you
    possibly care about it.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Sep 2 10:50:55 2025
    From Newsgroup: comp.lang.c

    On 01/09/2025 21:52, Michael S wrote:
    On Mon, 1 Sep 2025 11:28:15 +0200
    pozz <pozzugno@gmail.com> wrote:



    In the end, however, my guess is just that the limited coff/pe
    format used by Windows binaries is the issue.

    Ok.


    I find David Brown's explanation extremely unlikely.
    Microsoft's linker also generates COFF/PE format and it never had
    problems with linking in only the necessary stuff. They don't call it by fancy half-misleading names like 'garbage collection', in their world
    it is simply 'link' - a default "Release' policy since forever.

    If right now it does not work with Gnu ld then it happens most likely
    because COFF/PE is of low priority for Gnu ld devs rather than because COFF/PE is "limited".


    It is certainly plausible that it is the GNU ld support for COFF/PE that
    is limited here, rather than the COFF/PE format itself. I don't know
    enough details about the formats or the workings of GNU ld to judge.

    All I can say for sure is that the manual page for GNU ld says that
    garbage collection is "experimental" for COFF/PE targets, while it has
    worked fine for decades with elf formats. And that difference in
    formats looks like it is the root of Pozz's observations.

    That may be because of a lack of development priority in GNU ld for
    COFF/PE (though it supported COFF from before ELF was developed). After
    all, standard COFF /is/ severely limited in many ways (including the
    maximum number of sections - which will obviously be very large when -ffunction-sections and -fdata-sections are used), which is why most
    toolchain and OS vendors moved through various proprietary extensions to
    COFF before dropping it altogether for ELF.

    MS developed their own format PE as an extension and hybrid that grew
    out of COFF, and have added more and more features to it over the years.
    It is entirely believable to me that MS's own tools have better
    support for more advanced use of COFF/PE than GNU's.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Tue Sep 2 13:32:13 2025
    From Newsgroup: comp.lang.c

    On Tue, 2 Sep 2025 10:50:55 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 01/09/2025 21:52, Michael S wrote:
    On Mon, 1 Sep 2025 11:28:15 +0200
    pozz <pozzugno@gmail.com> wrote:



    In the end, however, my guess is just that the limited coff/pe
    format used by Windows binaries is the issue.

    Ok.


    I find David Brown's explanation extremely unlikely.
    Microsoft's linker also generates COFF/PE format and it never had
    problems with linking in only the necessary stuff. They don't call
    it by fancy half-misleading names like 'garbage collection', in
    their world it is simply 'link' - a default "Release' policy since
    forever.

    If right now it does not work with Gnu ld then it happens most
    likely because COFF/PE is of low priority for Gnu ld devs rather
    than because COFF/PE is "limited".


    It is certainly plausible that it is the GNU ld support for COFF/PE
    that is limited here, rather than the COFF/PE format itself. I don't
    know enough details about the formats or the workings of GNU ld to
    judge.

    All I can say for sure is that the manual page for GNU ld says that
    garbage collection is "experimental" for COFF/PE targets, while it
    has worked fine for decades with elf formats. And that difference in formats looks like it is the root of Pozz's observations.

    That may be because of a lack of development priority in GNU ld for
    COFF/PE (though it supported COFF from before ELF was developed).
    After all, standard COFF /is/ severely limited in many ways
    (including the maximum number of sections - which will obviously be
    very large when -ffunction-sections and -fdata-sections are used),
    which is why most toolchain and OS vendors moved through various
    proprietary extensions to COFF before dropping it altogether for ELF.

    MS developed their own format PE as an extension and hybrid that grew
    out of COFF, and have added more and more features to it over the
    years. It is entirely believable to me that MS's own tools have
    better support for more advanced use of COFF/PE than GNU's.


    As mentioned in my other message above, it turned out that gnu ld is
    not at fault. The problem is on compiler's (gcc) side. More
    specifically, the problem is somehow related to gcc implementation of asynchronous unwind tables. Format of the tables appears to be
    different between gcc-Linux, gcc-Windows and MSVC-Windows. I don't
    think that somebody would be surprised if the 2nd combo is the least deep-thought and least well-tested between the three.
    The machinery is hidden behind barely documented 'gnu as' directives
    like .seh_proc and .seh_endproc so pointing to exact mistake is not
    easy.
    Luckily for OP, disabling generation of asynchronous unwind tables *is*
    easy.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Sep 2 12:57:15 2025
    From Newsgroup: comp.lang.c

    On 01/09/2025 11:28, pozz wrote:
    Il 29/08/2025 15:04, David Brown ha scritto:
    On 29/08/2025 12:39, pozz wrote:
    Il 29/08/2025 11:05, David Brown ha scritto:
    On 28/08/2025 18:12, pozz wrote:
    I don't think this is a pure C programming question, but it's related. >>>>>
    I'm building a static library mylib.a with all the object files
    except main.o, and the final executable linking together main.o and >>>>> mylib.a.

    My first question is "Why?"  Why not simply link all the object
    files together?

    Because I want to start creating tests on my projects and one simple
    approach is what I have described. All is in a static library (a
    collection of object files), except main.o where is only main().

    If I want to create test1.c (with its main), I have to only link
    test1.o with library.


    The only "benefit" you get from using a library in this situation is
    that you might conceivably save a couple of lines in your makefile,

    Could you explain?

    Actually I'm using cmake and I write something similar to this for each test:

        add_executable(testA tests/testA.c)
        target_link_libraries(testA PRIVATE ${PRJLIB})
        target_link_options(testA PRIVATE -Wl,--gc-sections)  # doesn't work

    Without the library I need to explictly link testA.c with the exact
    source files required for the test. I should take care of compilation options, because they should be the same for the main executable and the test executable (otherwise I could test a different thing). Linking
    against the library helps on this point, because you are sure your
    testing exactly the production binary.


    I am not familiar enough with cmake to give a detailed answer. CMake
    has always struck me as two steps ahead of make, and six and a half
    steps behind it - I can't see any good justification or benefit that
    outweighs its disadvantages and complexities. Of course that view is
    highly subjective, based on what I need from a build tool, and my
    existing familiarity with make.

    But surely cmake has a convenient way of making a variable that is a
    list of all the object files in your source directory (based itself on a
    glob of all the C files in the source directory), and passing that to
    the linker command as easily as passing the name of a static library?


    and if you are using spinning rust drives and a PC from the 1990's,
    you might save a second or two from the build.  It is not worth the
    bother.

    No, it's not for that I'm using static library approach for testing.


    I don't see any reason for using a static library when building test
    binaries, compared to simply passing the list of object files to the linker.

    But I suspect you'd see the same effect (linker garbage collection not
    working as you want) whether you use a static library or individual
    object files.


    I want to remove from the final exe everything present in mylib.a
    that is not used in main.o.

    My second question is "Why?"  When you are working on embedded
    targets, saving space like this in the executable is often a very
    good idea.  On a PC, it is rarely worth the effort.

    This question was born because I found another problem.

    I have a library distributed in source codes. One function in one
    source code needs to access a global array that *must* be defined
    somewhere in the project.

    Okay.


    As I wrote, I'm creating some tests. Not all the tests use that
    function, so I thought it would be safe to not have the definition of
    the global array, but this isn't true in my project and building
    system. The linker complains because it doesn't find the global array.

    I expected the unused function, with its references, would have been
    removed by the linker.

    The "--gc-sections" gnu linker option works for elf files, but is
    "experimental" for coff/pe.  Maybe it simply doesn't work as
    effectively for mingw, which will be generating Windows-style coff/pe
    binaries, while it works for WSL which uses Linux-style elf binaries.

    Ok, I expected it was a very simple task for a linker. Don't put an
    unused section in the final binary.


    Cross-references back and forth can make it surprisingly complicated, I suspect. And it is not always a simple matter to determine which
    symbols must be kept, and which can be cleared out. Things that might
    cause complications include interrupt handlers or vectors, weak symbols
    and symbol aliasing. (Standard COFF does not, AFAIK, support weak
    symbols - COFF/PE does to some extent, but perhaps GNU ld does not
    support them with COFF.)


    But while I appreciate that you expected linker garbage collection to
    work here, you still haven't answered why you feel it is important.
    (If you are just trying to understand why it doesn't work, that's fine
    by me.)

    I explained below. Anyway I know of countermeasures to solve the
    specific problem, but my original question is only to understand what
    was happening and if I was wrong in something.

    Again, I thought the linker garbace collector was a simple task to do.


    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o mylib.a >>>>> objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc
    in WSL doesn't.

    Why?

    Why are you using "-ffunction-sections" and "-fdata-sections" ?

    Because each function is in its section and can be removed if unused
    by the garbage collector of the linker (at least, so I understood).

    They are potentially useful if you have a significant amount of extra
    code and/or data that is in your source code and not wanted in the
    final binary, /and/ that it is important for the binary to be as small
    as reasonably possible.

    Indeed they are used in embedded world, where the program memory is
    limited.


    I strongly recommend you be careful with these options, and think about
    what they actually do. In particular, -fdata-sections can make your
    code bigger and slower because it blocks the use of section anchors in a
    lot of cases. Yes, I know a lot of embedded developers use
    "-fdata-sections", and many IDE "wizards" enable it by default - they
    are wrong to do so.

    Imagine you have code like this :

    int x, int y, int z;
    int sum(void) { return x + y + z; }

    With -fdata-sections, the compiler has no idea where x, y, and z will
    end up - that is up to the linker. So on a load/store architecture
    (such as ARM), it must generate code approximating :


    int x, int y, int z;
    int sum(void) {
    const int * const px = &x;
    const int * const py = &y;
    const int * const pz = &z;
    const int tx = *px;
    const int ty = *py;
    const int tz = *pz;
    const int r1 = tx + ty;
    const int r2 = r1 + tz;
    return r2;
    }

    Without -fdata-sections, and without -fcommon (which is a terrible
    option for many reasons), the compiler can generate roughly :

    struct xyz {
    int x, int y, int z;
    } xyz;
    int sum(void) {
    const struct xyz * const pxyz = &xyz;
    const int tx = xyz->px;
    const int ty = xyz->py;
    const int tz = xyz->pz;
    const int r1 = tx + ty;
    const int r2 = r1 + tz;
    return r2;
    }

    This is significantly shorter and faster in practice - especially for
    targets that have a "load double register" instruction.


    -ffunction-sections is not such an issue for most microcontrollers, but
    can limit or reduce the effectiveness of some optimisations (like
    function cloning, hot/cold partitioning for cache usage, and branch and
    jump instruction size reduction for some targets).


    However, "-ffunction-sections" is not going to save you much space in
    your flash unless you have a lot of extra source code that is not
    actually needed in the program. Sometimes that can happen, especially
    if the same source code base is used with multiple build variants. But generally you don't want to write and test extra source code that you
    are not actually using in the binary. And if you /really/ want to
    squeeze for space, use LTO and you don't need -ffunction-sections or -fdata-sections - but LTO can be "fun" for debugging. (I often use -ffunction-sections because I do often have multiple build variants, but
    I actively avoid -fdata-sections.)



    As I understand it, you have an embedded program with lots of source
    code, and you want to test different parts of it by compiling with
    different small "main" functions on a PC.  I am not sure if you said
    the main program was for an embedded system, or if I am assuming that
    because you are one of the few people keeping comp.arch.embedded alive
    by starting new threads there :-)

    You have some relationship with Sherlock Homes :-)

    No. Sherlock Holmes uses extrapolation (despite entirely incorrectly
    claiming to use deductive logic) - he takes things he knows and follows outwards by patterns to guess other things. I used interpolation - I
    took things I knew and used the patterns to fill in the gaps. So I was
    quite confident in my guesses!


    Yes, you described my situation very well. I have the principal main for production exe (embedded target) and a few test mains.(native for dev machine).


    For the main program, function and data sections are probably not
    needed because you the source code that you build for the project is
    needed for the program - if there was a lot that you didn't need, it
    wouldn't be in the project build.

    Indeed "-ffunction-sections" and "-fdata-sections" aren't important for
    main target main.c.


    For the test programs, function and data sections are not needed
    because the size of the test binaries on the PC is irrelevant.

    Of course, except when there are some functions that I'm not testing and that need some references that are defined in the main main(), but not
    in the test main().


    That sounds like a code organisation issue. Basically, don't do that,
    and it will not be a problem. Inter-module dependencies should usually
    be a directed acyclic graph (a tree, without loops) with the "main"
    module at the root. (Sometimes cycles or loops are required, but they
    should be minimised.) The "main" module will depend, directly or
    indirectly, on all the rest of the code modules - other modules should
    not depend on the "main" module.


    Again - if your questions are from curiosity as to why things are not
    working as you expected, I fully appreciate that.

    Yes, mainly my question was for curiosity. I already fixed my test by defining unreference data in the test main, even if it isn't really
    needed in the test.


    If your questions are because you think there is a significant
    advantage in using static libraries and section garbage collection in
    your build process, I believe it is unlikely to be beneficial in
    reality - so it does not matter if they don't work.

    I think now is clear. I needed linker garbage collector to produce test binary just to fix the undeclared error.

    As I noted above, that is best solved in other ways.

    Regarding the benefecial of building a static library to link with main main.c and test main.c, I think it's a good approach yet.


    In the end, however, my guess is just that the limited coff/pe format
    used by Windows binaries is the issue.

    Ok.

    Or it is a limitation of GNU ld when using Windows COFF/PE.




    They can be helpful in some ways for omitting code and data that is
    defined in the code, but not actually used in the executable.  But
    they can also make linking slower, and "-fdata-sections" can reduce
    optimisations (especially if you have "-fcommon", which was the
    default in older gcc).


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From David Brown@david.brown@hesbynett.no to comp.lang.c on Tue Sep 2 13:03:09 2025
    From Newsgroup: comp.lang.c

    On 02/09/2025 12:32, Michael S wrote:
    On Tue, 2 Sep 2025 10:50:55 +0200
    David Brown <david.brown@hesbynett.no> wrote:

    On 01/09/2025 21:52, Michael S wrote:
    On Mon, 1 Sep 2025 11:28:15 +0200
    pozz <pozzugno@gmail.com> wrote:



    In the end, however, my guess is just that the limited coff/pe
    format used by Windows binaries is the issue.

    Ok.


    I find David Brown's explanation extremely unlikely.
    Microsoft's linker also generates COFF/PE format and it never had
    problems with linking in only the necessary stuff. They don't call
    it by fancy half-misleading names like 'garbage collection', in
    their world it is simply 'link' - a default "Release' policy since
    forever.

    If right now it does not work with Gnu ld then it happens most
    likely because COFF/PE is of low priority for Gnu ld devs rather
    than because COFF/PE is "limited".


    It is certainly plausible that it is the GNU ld support for COFF/PE
    that is limited here, rather than the COFF/PE format itself. I don't
    know enough details about the formats or the workings of GNU ld to
    judge.

    All I can say for sure is that the manual page for GNU ld says that
    garbage collection is "experimental" for COFF/PE targets, while it
    has worked fine for decades with elf formats. And that difference in
    formats looks like it is the root of Pozz's observations.

    That may be because of a lack of development priority in GNU ld for
    COFF/PE (though it supported COFF from before ELF was developed).
    After all, standard COFF /is/ severely limited in many ways
    (including the maximum number of sections - which will obviously be
    very large when -ffunction-sections and -fdata-sections are used),
    which is why most toolchain and OS vendors moved through various
    proprietary extensions to COFF before dropping it altogether for ELF.

    MS developed their own format PE as an extension and hybrid that grew
    out of COFF, and have added more and more features to it over the
    years. It is entirely believable to me that MS's own tools have
    better support for more advanced use of COFF/PE than GNU's.


    As mentioned in my other message above, it turned out that gnu ld is
    not at fault. The problem is on compiler's (gcc) side. More
    specifically, the problem is somehow related to gcc implementation of asynchronous unwind tables. Format of the tables appears to be
    different between gcc-Linux, gcc-Windows and MSVC-Windows. I don't
    think that somebody would be surprised if the 2nd combo is the least deep-thought and least well-tested between the three.
    The machinery is hidden behind barely documented 'gnu as' directives
    like .seh_proc and .seh_endproc so pointing to exact mistake is not
    easy.
    Luckily for OP, disabling generation of asynchronous unwind tables *is*
    easy.


    I should have read these other messages before replying!

    Thanks for the details here - it is always interesting to learn a little
    more, even if I do little C or C++ programming on Windows.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From antispam@antispam@fricas.org (Waldek Hebisch) to comp.lang.c on Tue Sep 2 17:57:42 2025
    From Newsgroup: comp.lang.c

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 1 Sep 2025 11:28:15 +0200
    pozz <pozzugno@gmail.com> wrote:



    In the end, however, my guess is just that the limited coff/pe
    format used by Windows binaries is the issue.

    Ok.


    I find David Brown's explanation extremely unlikely.
    Microsoft's linker also generates COFF/PE format and it never had
    problems with linking in only the necessary stuff. They don't call it by fancy half-misleading names like 'garbage collection', in their world
    it is simply 'link' - a default "Release' policy since forever.

    If right now it does not work with Gnu ld then it happens most likely
    because COFF/PE is of low priority for Gnu ld devs rather than because COFF/PE is "limited".

    Are you sure that Microsoft's linker is doing the same thing?
    Liners for ages were able to skip "unused" object files and AFAIK
    GNU linker on any platform can do this. However, to make this
    effective one needs tiny object files: one object file per
    function or variable. In case of GNU tools tiny object files
    may imply tiny source files, which is inconvenient. The
    '--function-sections' and '--data-sections' stuff means that
    compiler is splitting files into tiny parts. Not a problem
    when you have proprietary format, but otherwise it requires
    proper annotations for assembler and some way to represent them
    in output format (which may be undocumented feature used by
    vendor tools). I do not know details of COFF/PE, but some
    formats treat code part as a single blob, which means that
    info needed to split code into functions is normally not
    available in that format.
    --
    Waldek Hebisch
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Michael S@already5chosen@yahoo.com to comp.lang.c on Tue Sep 2 22:27:33 2025
    From Newsgroup: comp.lang.c

    On Tue, 2 Sep 2025 17:57:42 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Are you sure ... <skip>

    Please read more recent parts of the thread.
    The problem is not caused by linker, but by compiler.


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From pozz@pozzugno@gmail.com to comp.lang.c on Wed Sep 3 13:02:12 2025
    From Newsgroup: comp.lang.c

    Il 02/09/2025 01:16, Michael S ha scritto:
    On Tue, 2 Sep 2025 02:02:20 +0300
    Michael S <already5chosen@yahoo.com> wrote:

    On Thu, 28 Aug 2025 18:12:52 +0200
    pozz <pozzugno@gmail.com> wrote:

    I don't think this is a pure C programming question, but it's
    related.

    I'm building a static library mylib.a with all the object files
    except main.o, and the final executable linking together main.o and
    mylib.a.

    I want to remove from the final exe everything present in mylib.a
    that is not used in main.o.

    Suppose only mod1.o is present in mylib.a. foo1() and bar1() are
    defined in mod1.c. main() is the only function in main.c and only
    foo1() is called from main().

    gcc -O2 -ffunction-sections -fdata-sections -c -o mod1.o mod1.c
    ar rcs mylib.a mod1.o
    gcc -O2 -ffunction-sections -fdata-sections -c -o main.o main.c
    gcc -Wl,--gc-sections,--print-gc-sections -o main[.exe] main.o
    mylib.a objdump -d main[.exe] | grep bar1

    MinGW in Windows build a main.exe that contains bar1(), while gcc in
    WSL doesn't.

    Why?

    I investigated it a little more and found two things:
    1. The problem caused by gcc compiler rather than by gnu ld
    linker. If you link MSVC-generated object files with gnu ld then
    "garbage collection" works as expected.
    2. The problem is related to gcc implementation of SEH.
    Compile to asm, edit asm removing SEH directives then assemble to .o
    and it works as expected.

    Now in order to continue from here I'd have to know much more
    about SEH than I actually do. So, I stopped.





    I still don't know much about SEH, but now, due to Stack Overflow, I
    know how to get rid of it.
    Just add -fno-asynchronous-unwind-tables to your compilation options
    and suddenly 'garbage collection' works.
    Of course, it helps only if you don't care about SEH. But, considering
    that what you are doing are mere unit tests, I don't see why would you possibly care about it.

    Thank you Michael, it works!

    --- Synchronet 3.21a-Linux NewsLink 1.2