• [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?"-a 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?"-a When you are working on embedded targets, saving space like this in the executable is often a very good idea.-a 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.-a 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?"-a 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?"-a When you are working on embedded
    targets, saving space like this in the executable is often a very good
    idea.-a 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.-a 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?"-a 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.-a 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?"-a When you are working on embedded
    targets, saving space like this in the executable is often a very
    good idea.-a 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.-a 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.-a (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.-a 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.-a 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