In addition to the data some metadata must be communicated betwen the application, transport and internet layers.
Technically how metadata is communicated between layers is an implementation detail. In practice the application layer nearly always uses some variant of the berkerly sockets API to talk to the transport layer.
For TCP clients the destination IP and port are specified to the transport layer as part of the "connect" API call. For UDP clients either "connect" can be used to create a psuedo-connnection or the destination IP and port can be specified on a per-packet basis with the "sendto" api call.
For TCP servers the application can read the IP and port by using by calling getpeername after accepting a connection. UDP servers can read the IP and port for each packet by reading packets using the recvfrom API call.
Unfortunately sendto and recvfrom have a design flaw. They only pass the remote address, not the local one which can cause problems for servers on multihomed hosts. The server may send replies from the wrong IP address causing them to be dropped, either by the network or the client. There are newer APIs to deal with this but the details vary between operating systems.
The transport layer will in turn inform the internet layer of the IP addresses for outgoing packets and the internet layer will inform the transport layer of the IP addresses for incoming packets. Since both the transport and internet layers are typically part of the TCP/IP stack the details of how this is done is an implementation detail inside the stack.
x-forwarded-for is a http header used by http proxies. The proxy will retrieve the client IP address using getpeername, it will then encode it into a http header to pass it on to the next server.