Solved Linker issue with hello world C program?

I believe I'm not seeing the forest for the trees and need some help. Can anybody tell me, why my incredibly simple C program fails to link?
#include <stdio.h>
#include <stdlib.h>

int main( int argc, char **argv ) {
    printf( "Hello, world!\n" );


.ifdef RELEASE
CCARGS=$(DEBUG) -c -Wall -pedantic -std=c99

LDARGS=-dynamic-linker --dy -L /usr/lib -L /lib -lc


all: main
        echo Compilation completed.

main.o: main.c
        $(CC) $(CCARGS) -o main.o main.c

main: main.o
        $(LD) $(LDARGS) -o main main.o

Compiling works with clang bug ld gives me
ld -dynamic-linker --dy -L /usr/lib -L /lib -lc -o main main.o
ld: error: /lib/ undefined reference to __progname
ld: error: /lib/ undefined reference to environ
*** Error code 1

I don't get it. When I run clang -o test main.o I get an ELF executable that's also only linked to libc. So why the heck is ld failing?
% ldd test 
test: => /lib/ (0x80024c000)
Gaah, alright. Simply run clang with -v -v -v and you get all the options that I was missing. Duh:

FreeBSD clang version 10.0.1 ( llvmorg-10.0.1-0-gef32c611aa2)
Target: x86_64-unknown-freebsd12.2
Thread model: posix
InstalledDir: /usr/bin
 "/usr/bin/ld" --eh-frame-hdr -dynamic-linker /libexec/ --hash-style=both --enable-new-dtags -o test /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o -L/usr/lib main.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o

To be honest. Those options were very simple (basically just a couple -L and -lc) in the beginning. It just didn't link. Now it appears it won't work without all those object files that clang pulled in as well.
Just for reference - this is, what I got without those bunch of additional arguments:
% ld -L/usr/lib -L/lib -lc main.o
ld: error: /lib/ undefined reference to __progname
ld: error: /lib/ undefined reference to environ

It works with the options above. So I'll flag this as resolved for now. Maybe someone has some reduced set of linker options, I'd be happy to go with a simpler Makefile. I don't feel like trial and error and will just use the clang provided ones for the moment.
Yep, I'm with kpedersen in questioning what you're trying to achieve. --dynamic-linker should have a path to a linker. You're using a makefile for building a shared library by the looks of its origin. That certainly doesn't correlate with your successful cc command line build.
mark_j, you're right. That --dynamic-linker needs a parameter. It probably is superfluous; this option came from clang and uses linker /libexec/ which - I suppose - is correct. Those flags didn't come from me.

"/usr/bin/ld" --eh-frame-hdr -dynamic-linker /libexec/ --hash-style=both --enable-new-dtags -o test /usr/lib/crt1.o /usr/lib/crti.o /usr/lib/crtbegin.o -L/usr/lib main.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/crtend.o /usr/lib/crtn.o

It's basically what kpedersen suggested - I ran clang (with -v -v) instead of ld, which then ran ld for me.

And yes, for a such a simple program, I shouldn't need to call ld separately; one can just as well do clang in one go. The idea of that exercise was to prepare for a more complicated setup down the road, though.

I haven't done any C programming for a while; last time I didn't need any crt object files to link - maybe that changed with clang. First time using clang for me, so thig might just come with the territory.
I thought the flags did come from you because you had it in the Makefile:
LDARGS=-dynamic-linker --dy -L /usr/lib -L /lib -lc

Sorry, I misunderstood.

Anyway, It should be -dynamic-linker.

IMO you're best to leave ld.lld(1) alone unless you're performing something non-standard, for example, relocatable shared objects, static linking etc.

Just let clang/cc do it for you, especially until you get your C-legs... 😎
Ah, right. Now I see it, too. Yup, that Makefile up there is wrong.

Also, I think the dynamic linker option requires a double dash. Maybe that was just ignored. Well, I said it before: forest for the trees...
That said, I recall something like the following working just fine on GCC:

$ gcc -c main.c
$ ld main.o -oprog

Whereas on clang, you need to pass in some flags* to ld in order to link a simple executable. Possibly because Clang is a cross compiler whereas individual builds of GCC aren't so naturally it is more complex because you need to tell it about the host.

*cc (clang) invokes it as:
"/usr/bin/ld" -e __start --eh-frame-hdr -Bdynamic -dynamic-linker /usr/libexec/ -o a.out /usr/bin/../lib/crt0.o /usr/bin/../lib/crtbegin.o -L/usr/bin/../lib -L/usr/lib -L/usr/X11R6/lib -L/usr/local/lib main.o -lcompiler_rt -lc -lcompiler_rt /usr/bin/../lib/crtend.o
I am coming in late on this thread.

For a hello world merely run:
cc hw.c (or whatever name you have given your C file)

That's all you need. No errors.
Sometimes an exercise like this also about gettng the Makefile correct. Next steps are typically "who do I compile a bunch of files in a subdirectory, put the output in a targeted directory based on machine characteristics and create a shared library from them that can be linked to other stuff. Oh and create install and clean targets for everything".

I'm not saying anything about the grammar corrections, except "They made me smile"
Why are you making things unnecessarily complicated?

Just about all 'make' tools that I know of use some fairly standard make variables:
CPPFLAGS for the preprocessor, usually -D and -I options
CFLAGS flags for the C compiler, stuff like optimization/debug and warning flags
LDFLAGS for the linker

Unless you have a really good reason then I recommend that you stick to using these.

Last thing, I would also use the C compiler (or C++ compiler if you have any C++ code in your project) as the linker driver. I'm not on a FreeBSD box atm, but GNU make has the following builtin variables


#  recipe to execute (built-in):
        $( $^ $(LOADLIBES) $(LDLIBS) -o $@

%: %.c
#  recipe to execute (built-in):
        $(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $@