This is something that have always puzzled me. I understand what is an assembler, compiler, and executable file, and I also have a good concept of what is an executable added with debug info.
So far my understanding is the following. A "modern" assembler does not generate executable code straight, as it was the case for very simple/naive assemblers (where .org statements were your only control of where code is going to be located, and where there was never a debugger).
More modern assemblers instead produces an "object file", which is a mix of executable code but with a header, lacking references (it shows all 0x00 in hexadecimal), and debug labels at the end, so that other programs can link with this one, or even call this file directly while executing (which is the case of .dll files on windows).
Now that was for the part I understood. From now on there is things I'm not too sure about :
The role of the program called "ld" is to deal with those object files. It is possible to combine multiple object files into one, and do this again, again and again it one wants. A simple ld call on a single object file would simply produces the exact same file at output. On every (explicit or implicit) call of ld, a linkscript have to be provided, or else ld don't know where to put which section in memory when resolving the labels.
If a linkscript is not provided, some "default linkscript" whch is either hard coded into ld or located somewhere I have no idea where, will be used. In the case of computer program development, one does not have to worry about linkscripts at all, as everyting will do fine with the default one. On the other side, when doing embedded development, it is extremely unlikely that the default linkscript describes your target sytem correctly. In this case it is necessary to write a good link-script, or to use some kind of development kit which will hide it from you (it is sad but apparently this solution is the most common one, at least 90% of programmers would have no idea a link script even exists, myself I didn't know they existed until last month).
Apparently, under certain condition, ld produces a "final" executable, which is stripped of the debug labels and header. Then the output can't be linked any more. I'm not too sure when this happens or how this is done. For this reason, at least one call to ld has to be done when compiling a program. If a call to gcc is done without "-S" or "-c" a ghost call to ld will be done without the user even knowing it. The only valid reason I see as why they did this is to simplify the compilation of very simple single-file programs.
On the other hand, objcopy is almost the same as ld. The only difference is that it can only take a single object file as input, while ld can take many.
Therefore I don't see the use of objcopy at all.
So far my understanding is the following. A "modern" assembler does not generate executable code straight, as it was the case for very simple/naive assemblers (where .org statements were your only control of where code is going to be located, and where there was never a debugger).
More modern assemblers instead produces an "object file", which is a mix of executable code but with a header, lacking references (it shows all 0x00 in hexadecimal), and debug labels at the end, so that other programs can link with this one, or even call this file directly while executing (which is the case of .dll files on windows).
Now that was for the part I understood. From now on there is things I'm not too sure about :
The role of the program called "ld" is to deal with those object files. It is possible to combine multiple object files into one, and do this again, again and again it one wants. A simple ld call on a single object file would simply produces the exact same file at output. On every (explicit or implicit) call of ld, a linkscript have to be provided, or else ld don't know where to put which section in memory when resolving the labels.
If a linkscript is not provided, some "default linkscript" whch is either hard coded into ld or located somewhere I have no idea where, will be used. In the case of computer program development, one does not have to worry about linkscripts at all, as everyting will do fine with the default one. On the other side, when doing embedded development, it is extremely unlikely that the default linkscript describes your target sytem correctly. In this case it is necessary to write a good link-script, or to use some kind of development kit which will hide it from you (it is sad but apparently this solution is the most common one, at least 90% of programmers would have no idea a link script even exists, myself I didn't know they existed until last month).
Apparently, under certain condition, ld produces a "final" executable, which is stripped of the debug labels and header. Then the output can't be linked any more. I'm not too sure when this happens or how this is done. For this reason, at least one call to ld has to be done when compiling a program. If a call to gcc is done without "-S" or "-c" a ghost call to ld will be done without the user even knowing it. The only valid reason I see as why they did this is to simplify the compilation of very simple single-file programs.
On the other hand, objcopy is almost the same as ld. The only difference is that it can only take a single object file as input, while ld can take many.
Therefore I don't see the use of objcopy at all.