You are starting from a bad premise:
I understand how an address space is partitioned into: code, data, stack and heap. However, I am having trouble mapping what goes to where for a given C code.
The address space is not partitioned in this way. The address space contains memory. The memory used for stack is indistinguishable from memory used for heap. The only thing that makes a stack a stack is that it is being allocated in the application using a stack pointer. The only thing that makes a heap a heap is that that there is a heap manager in the application. You could allocate memory from a heap and assign it to the the hardware stack pointer and your memory is both heap and stack.
Here is another misconception:
I know that: global variables are in the data section. static variables are in the data section. local variables are in the stack section. dynamic allocated space are in the heap section.
How things worker differs among assemblers, linkers, and systems. However, rational assemblers allow the user to define their own named sections. In many assemblers, I could create Bobs_Data_Section, Freds_Data_Section, and Sams_Data_section and put global variables in each of them.
With most (but not all) compilers, the programmer has no control over how it is going to create sections. There is no guarantee that a compiler is going to put global variables in a section called "data." In fact, global variables and static local variables could be in the same section.
Such "Sections" are generally only input to the linker. The linker groups sections defined by the assembler and compiler together into memory regions with common access attributes. What goes into the linker as, say, a "data" section comes out of the linker as instructions in the executable to create pages that are read/write.
My questions is, when including a library into a program, where does it place in an address space?
So now you have hit the problem of how you are trying to learn how things work. If you view the process address space as just being memory, you can load the library anywhere. The program loader reads the instructions in the executable file and creates pages with the correct attributes (e.g., readonly/noexecute, read/write, readonly/execute) anywhere available in the address space.
If you view the address space as being partitioned into code, data, etc. loading libraries becomes problematic. Which makes me wonder why schools and books persist in teaching using these nonsensical concepts.