You've got the broad strokes correct, I'll try to break down each part of your question and point you in the right direction.
To use pthread_create and other POSIX thread library functions, we need this flag. Why do we need this?
pthreads are not implemented as gcc builtins or as part of "standard" libc, which means an external library must implement them. Since an external library is required, the linker needs to be informed about that external library with the -pthread
flag.
Why isn't there a code in /usr that implements these functions like other functions or implementation of other system calls?
There absolutely is, the pthread API is implemented in libpthread, which you will almost certainly find in its shared (libpthread.so), static, (libpthread.a), and version specific (libpthread-X.YY.so) forms in your /usr/lib folder.
Also, without this flag, following output is there: ERROR
This is the linker telling you that you haven't specified an implementation of pthread for it to use. Just because glibc provides an implementation does not mean that is the implementation you intend to use. The linker is not a mind reader, it needs to be informed at compile time what specific libraries you want to link to.
My question is, why are we getting these errors (undefined reference to...) at compile time? gcc should make the executable and during run-time, it should try to find these symbols right? (Dynamic linking)
Once again, the linker needs to know at compile time specifically what library it will look for at run time. The ABIs of two libraries that define the same symbols may not be the same (and likely won't be, unless specifically designed for). That means even though your dynamically linked code won't carry a statically linked copy of the library, the binary structure of the code still depends on the library dependency. This information therefore must be known at compile time.
NOTE: These symbols (pthread_create, pthread_join) are extern (checked in preproc using -E gcc flag). Is that different from symbols loaded at run-time?
No, extern
just informs the compiler that a symbol won't be defined in a given compilation unit. If that doesn't make sense, it basically means, "that symbol is defined in another file, I'm just using it here".