Linking is the final stage of the gcc compilation process.

In the linking process, object files are linked together and all the references to external symbols are resolved, final addresses are assigned to function calls, etc.

In this article we will mainly focus on the following aspects of gcc linking process:

  1. Object files and how are they linked together
  2. Code relocations


Before you read this article, make sure you understand all the 4 stages that a C program has to go through before becoming an executable (pre-processing, compilation, assembly and linking).

LINKING OBJECT FILES

Lets understand this first step through an example. First create the following main.c program.

$ vi main.c
#include <stdio.h> 

extern void func(void); 

int main(void)
{
    printf("\n Inside main()\n");
    func(); 

    return 0;
}

Next create the following func.c program. In the file main.c we have declared a function func() through keyword ‘extern’ and have defined this function in a separate file func.c

$ vi func.c
void func(void)
{
    printf("\n Inside func()\n");
}

Create the object file for func.c as shown below. This will create the file func.o in the current directory.

$ gcc -c func.c

Similarly create the object file for main.c as shown below. This will create the file main.o in the current directory.

$ gcc -c main.c

Now execute the following command to link these two object files to produce a final executable. This will create the file ‘main’ in the current directory.

$ gcc func.o main.o -o main

When you execute this ‘main’ program you’ll see the following output.

$ ./main
Inside main()
Inside func()

From the above output, it is clear that we were able to link the two object files successfully into a final executable.

What did we acheive when we separated function func() from main.c and wrote it in func.c?

The answer is that here it may not have mattered much if we would have written the function func() in the same file too but think of very large programs where we might have thousands of lines of code. A change to one line of code could result in recompilation of the whole source code which is not accceptable in most cases. So, very large programs are sometimes divided into small peices which are finaly linked together to produce the executable.

The make utility which works on makefiles comes into the play in most of these situations because this utility knows which source files have been changed and which object files need to be recompiled. The object files whose corresponding source files have not been altered are linked as it is. This makes the compilation process very easy and manageable.

So, now we understand that when we link the two object files func.o and main.o, the gcc linker is able to resolve the function call to func() and when the final executable main is executed, we see the printf() inside the function func() being executed.

Where did the linker find the definition of the function printf()? Since Linker did not give any error that surely means that linker found the definition of printf(). printf() is a function which is declared in stdio.h and defined as a part of standard ‘C’ shared library (libc.so)

We did not link this shared object file to our program. So, how did this work? Use the ldd tool to find out, which prints the shared libraries required by each program or shared library specified on the command line.

Execute ldd on the ‘main’ executable, which will display the following output.

$ ldd main
linux-vdso.so.1 =>  (0x00007fff1c1ff000)
libc.so.6 => /lib/libc.so.6 (0x00007f32fa6ad000)
/lib64/ld-linux-x86-64.so.2 (0x00007f32faa4f000)

The above output indicates that the main executable depends on three libraries. The second line in the above output is ‘libc.so.6′ (standard ‘C” library). This is how gcc linker is able to resolve the function call to printf().

The first library is required for making system calls while the third shared library is the one which loads all the other shared libraries required by the executable. This library will be present for every executable which depends on any other shared libraries for its execution.

During linking, the command that is internally used by gcc is very long but from users prespective, we just have to write.

$ gcc <object files> -o <output file name>

CODE RELOCATION

Relocations are entries within a binary that are left to be filled at link time or run time. A typical relocation entry says: Find the value of ‘z’ and put that value into the final executable at offset ‘x’

Create the following reloc.c for this example.

$ vi reloc.c
extern void func(void); 

void func1(void)
{
    func();
}

In the above reloc.c we declared a function func() whose definition is still not provided, but we are calling that function in func1().

Create an object file reloc.o from reloc.c as shown below.

$ gcc -c reloc.c -o reloc.o

Use readelf utility to see the relocations in this object file as shown below.

$ readelf --relocs reloc.o
Relocation section '.rela.text' at offset 0x510 contains 1 entries:
Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000005  000900000002 R_X86_64_PC32     0000000000000000 func - 4
...

The address of func() is not known at the time we make reloc.o so the compiler leaves a relocation of type R_X86_64_PC32. This relocation indirectly says that “fill the address of the function func() in the final executable at offset 000000000005”.

The above relocation was corresponding to the .text section in the object file reloc.o (again one needs to understand the structure of ELF files to understand various sections) so lets disassemble the .text section using objdump utility:

$ objdump --disassemble reloc.o
reloc.o:     file format elf64-x86-64 

Disassembly of section .text: 

0000000000000000 <func1>:
   0:	55                   	push   %rbp
   1:	48 89 e5             	mov    %rsp,%rbp
   4:	e8 00 00 00 00       	callq  9 <func1+0x9>
   9:	c9                   	leaveq
   a:	c3                   	retq

In the above output, the offset ’5′ (entry with value ’4′ relative to starting address 0000000000000000) has 4 bytes waiting to be writen with the address of function func().

So, there is a relocation pending for the function func() which will get resolved when we link reloc.o with the object file or library that contains the defination of function func().

Lets try and see whether this relocation gets reolved or not. Here is another file main.c that provides defination of func() :

$ vi main.c
#include<stdio.h> 

void func(void) // Provides the defination
{
    printf("\n Inside func()\n");
} 

int main(void)
{
    printf("\n Inside main()\n");
    func1();
    return 0;
}

Create main.o object file from main.c as shown below.

$ gcc -c main.c -o main.o

Link reloc.o with main.o and try to produce an executable as shown below.

$ gcc reloc.o main.o -o reloc

Execute objdump again and see whether the relocation has been resolved or not:

$ objdump --disassemble reloc > output.txt

We redirected the output because an executable contains lots and lots of information and we do not want to get lost on stdout.
View the content of the output.txt file.

$ vi output.txt
...
0000000000400524 <func1>:
400524:       55                      push   %rbp
400525:       48 89 e5                mov    %rsp,%rbp
400528:       e8 03 00 00 00          callq  400530 <func>
40052d:       c9                      leaveq
40052e:       c3                      retq
40052f:       90                      nop
...

In the 4th line, we can clearly see that the empty address bytes that we saw earlier are now filled with the address of function func().

To conclude, gcc compiler linking is such a vast sea to dive in that it cannot be covered in one article. Still, this article made an attempt to peel off the first layer of linking process to give you an idea about what happens beneath the gcc command that promises to link different object files to produce an executable.

'Linux > C/C++' 카테고리의 다른 글

memory lock  (0) 2012.07.16
C에서 제공하는 메모리 정렬 및 검색 함수들  (0) 2012.07.16
리눅스 명령어 : readelf  (0) 2012.07.16
variable length array  (0) 2012.07.16
nslookup -> getaddrinfo 사용 사례  (0) 2012.07.16

ELF 는 실행 가능한 바이너리 또는 오브젝트 파일 등의 형식을 규정한 것입니다. ELF 파일은 ELF 헤더가 맨 앞에 위치하고, 프로그램 헤더 테이블과 섹션 헤더 테이블이 그 뒤에 위치한다. (사실 더 복잡한 구조지만 큰 구조가 그렇다는 이야기)

ELF 헤더는 파일이 ELF 포맷일 경우 파일 맨앞에 반드시 존재하며, readelf -h 명령어로 elf의 헤더를 볼 수 있습니다. 명령어 참~ 쉽죠? read elf 입니다. 기억하기 쉬워요. 파일 분석시 굉장히 많이 사용하는 명령어 입니다.

사용자 삽입 이미지

Entry point Address - 시작하는 가상주소를 나타낸다.



프로그램 헤더는 readelf -l '파일명' 으로 섹션 헤더는 readelf -S '파일명'으로 확인 가능하다.

사용자 삽입 이미지

shstrtab


위 그림에서 빨간색 네모로 쳐진 shstrtab 의 type 항목을 보면 String table 이라는 것을 알 수 있다. base address 는 0x00ff0c 크기는 0x0000c7 인 것을 알 수 있다. od --skip-bytes 0x00ff0c --read-bytes 0xc7 -t x1z -A x zip 이라고 입력하면 shstrtab 의 내용을 볼 수 있다.

심볼 테이블은 readelf -s 파일명으로 볼 수 있다.

배운 명령어
  • readelf -h (ELF 헤더 확인) 
  • readelf -l (Program 헤더 확인) 
  • readelf -S (Section 헤더 확인)
  • readelf -s (심볼 테이블 확인)
  • od --skip-bytes 0x00ff0c --read-bytes 0xc7 -t x1z -A x zip


variable length array Linux/C/C++ 2012. 7. 16. 10:08

C에서는 가변길이의 배열을 잡을 수 없다는 사실이 아주 오래전 부터 머리속에 각인되어 있었다.

즉,

void makeArr(int number)
{
int arr[number];
....
....
}

위와 같은 형태로는 배열을 생성할 수 없다.
배열의 길이를 런타임에 결정하기 위해서는 malloc() 함수를 사용해야 한다.

int* arr = (int)malloc(sizeof(int)*number);

위오 같은 형태로 배열을 생성해 줘야 런타임에 배열의 길이를 정하여 생성할 수 있었다.


근데!!! 이게 바꼈단다.
언제부터인지는 모르지만 C99의 표준을 반영한 컴파일러는 Variable Length Array 를 지원한단다.
즉,

int arr[number];

와 같은 형태로 선언하여 컴파일 해도 전혀 문제가 되지 않는다는 얘기다.
확인하기 위해 현재 내 컴퓨터에 깔려 있는 GCC 4.4.1 버전으로 테스해 봤더니 정상적으로 동작한다.
C99이 99년도에 나왔다는데 평소 C를 전혀 사용하지 않으니.. 관심도 없었나 보다.

참고로 MS계열의 컴파일러는 이를 지원하지 않는다. Visual Studio 2008 에서 테스트 해 봤지만 컴파일 에러를 뿜었다.


컴파일 옵션 : --std=c99

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
 
 
int main(int argc, char **argv)
{
        struct addrinfo hints;
        struct addrinfo *result, *rp;
        struct sockaddr_in *sin;
        struct sockaddr_in6 *sin6;
        int *listen_fd;
        int listen_fd_num=0;
 
        char buf[80] = {0x00,};
        int i = 0;
       /* if(argc != 2)
        {
                printf("Usage : %s [port]\n", argv[0]);
                return 1;
        }
     */

        memset(&hints, 0x00, sizeof(struct addrinfo));
 
        hints.ai_flags = AI_PASSIVE;
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
 
        if(getaddrinfo(argv[1], NULL, &hints, &result) != 0 )
        {
                perror("getaddrinfo");
                return 1;
        }
 
        for(rp = result ; rp != NULL; rp = rp->ai_next)
        {
                listen_fd_num++;
        }
        listen_fd = malloc(sizeof(int)*listen_fd_num);
 
        printf("Num %d\n", listen_fd_num);
        for(rp = result, i=0 ; rp != NULL; rp = rp->ai_next, i++)
        {
                if(rp->ai_family == AF_INET)
                {
                        sin = (void *)rp->ai_addr;
                        inet_ntop(rp->ai_family, &sin->sin_addr, buf, sizeof(buf));
                        printf("<bind 정보 %d %d %s>\n", rp->ai_protocol, rp->ai_socktype, buf);
                }
                else if(rp->ai_family == AF_INET6)
                {
                        sin6 = (void *)rp->ai_addr;
                        inet_ntop(rp->ai_family, &sin6->sin6_addr, buf, sizeof(buf));
                        printf("<bind 정보 %d %d %s>\n", rp->ai_protocol, rp->ai_socktype, buf);
                }
                if((listen_fd[i] = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol)) < 0)
                {
                        printf("Socket Create Error\n");
                }
                if(rp->ai_family == AF_INET6)
                {
                        int opt = 1;
                        setsockopt(listen_fd[i], IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, sizeof(opt));
                }
 
                if(bind(listen_fd[i], rp->ai_addr, rp->ai_addrlen) != 0)
                {
                        if(errno != EADDRINUSE);
                        {
                                perror("bind error\n");
                                return 1;
                        }
                }
                if(listen(listen_fd[i], 5) != 0)
                {
                        perror("listen error\n");
                        return 1;
                }
        }
        freeaddrinfo(result);
        pause();
        return 1;
}

'Linux > C/C++' 카테고리의 다른 글

리눅스 명령어 : readelf  (0) 2012.07.16
variable length array  (0) 2012.07.16
LFS (Large File Support)  (0) 2012.07.11
C 언어 레퍼런스 - setvbuf 함수  (0) 2012.07.11
recvmsg sendmsg example from italy  (0) 2012.07.11

리눅스에서 LFS(Large File Support) 를 구현하는 방법은 두가지가 있습니다.
1) 32bit 와 64bit 파일관련 함수를 나눠서 쓰는 경우

#define _LARGEFILE_SOURCE
#define _LARGEFILE64_SOURCE

위의 두가지를 define 해주고 사용합니다. 그러면 open(), read(), write() ... 는 32bit 함수로
open64(), read64(), write64() ... 는 64bit 함수로 사용됩니다.
64bit 함수들은 64bit 형식의 off64_t 형을 오프셋으로 사용합니다.
그리고 64bit 형의 파일 탐색은 ftello(), fseeko() 함수를 대신 사용합니다.

ps : off_t -> of64_t

open() -> opne64() .....

2) 기존의 32bit 함수를 모두 64bit 로 전환
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64

위의 두가지를 define 하면 open(), read(), write() 등이 모두 64bit 형으로 작동합니다.
따라서 off_t 를 사용해도 자동적으로 off64_t 로 전환됩니다.
마찬가지로 64bit 형의 파일 탐색은 ftello(), fseeko() 함수를 사용합니다.


http://www.suse.de/~aj/linux_lfs.html

'Linux > C/C++' 카테고리의 다른 글

variable length array  (0) 2012.07.16
nslookup -> getaddrinfo 사용 사례  (0) 2012.07.16
C 언어 레퍼런스 - setvbuf 함수  (0) 2012.07.11
recvmsg sendmsg example from italy  (0) 2012.07.11
Socket Programming (sendmsg(), recvmsg() example)  (0) 2012.07.11
#include <stdio.h> // C++ 에서는 <cstdio>

int setvbuf ( FILE * stream, char * buffer, int mode, size_t size );

스트림 버퍼링 방식을 변경한다.
버퍼를 특정한 스트림의 입출력 연산에 사용될 수 있도록 변경한다. 이 함수는 버퍼링 방식과 버퍼의 크기를 설정할 수 있게 지원한다.
버퍼의 크기는 세번째 인자에 바이트 수로 전달된다.
만일 버퍼를 설정하지 않는다면 (즉, 두 번째 인자가 NULL 이라면), 시스템은 동적으로, 함수에 의해 요청된 크기 만큼 메모리를 할당하게 되며 이를 스트림의 버퍼로 사용하게 된다.
mode 인자는 이 버퍼를 fully buffered, line buffered, 아니면 unbuffered 로 할 지 결정한다.

fully buffered 스트림의 경우 읽기 작업은 스트림에 대응되는 장비에 바로 쓰여지지 않는다. 그 대신에 쓰여질 데이터는 버퍼에 잠시 저장되었다가 버퍼에 일정한 블록 이상 쌓이게 되면 그제서야 장비에 쓰여지게 된다. 물론, 한 블록이 다 채워지지 않았는데도 불구하고 스트림을 강제로 비움으로써 (flush) 장비에 쓸 수 있는데 이는 fflush 함수를 호출하거나 fclose 함수를 호출해 파일을 닫으면 된다. 참고적으로 모든 버퍼는 프로그램이 종료시 자동적으로 비워지게 된다.

line buffered 스트림의 경우 버퍼에 개행 문자가 입력될 때 마다 장비에 쓰여지게 된다.

unbuffered 스트림의 경우 데이터는 버퍼와 같은 중간 경유지를 거치지 않고 직접적으로 쓰여지게 된다. 즉, 쓰기 작업을 할 때 바로바로 쓰이게 된다.

모든 열려진 파일들은 기본적으로 할당된 버퍼를 가지고 있다. 이 함수는 그렇게 할당된 버퍼의 크기를 재조정 하거나, 사용자가 특별히 할당한 버퍼로 버퍼를 바꾸거나, 파일의 버퍼링 방식을 변경할 수 있게 해준다. 표준 출력(stdout) 이나 표준 오류(stderr) 같은 시스템 표준 스트림은 리다이렉트(redirect) 되지 않는 한 기본적으로 unbuffered 로 설정되어 있다.

   인자
 

stream

작업을 수행할 열린 스트림의 FILE 객체를 가리키는 포인터.

buffer

유저가 할당한 버퍼로 최소한 1 바이트의 크기는 되야 한다.
만일 NULL 로 설정하면 함수는 자동으로 지정한 크기의 버퍼를 할당하게 된다

mode

파일 버퍼링의 형식을 지정한다.
_IOFBF 완전한 버퍼링(Full Buffering): 앞에서 설명한 fully buffered 스트림을 일컫는다.
_IOLBF 행 버퍼링(Line Buffering): 출력시, 버퍼가 채워지거나 스트림에 개행 문자가 입력되었다면 데이터가 버퍼에서 출력된다. 입력 시에는 버퍼가 개행 문자를 만날 때 까지 버퍼를 채우게 된다.
_IONBF 버퍼링 사용 안함(No Buffering): 버퍼를 사용하지 않는다. 각각의 입출력 작업은 버퍼를 지나지 않고 요청 즉시 진행되며 이 겨우 buffer 인자와 size 인자는 모두 무시된다.

size


지정할 버퍼의 크기로 단위는 바이트 이다. 이 때, 버퍼로 지정한 배열의 크기 보다 반드시 작거나 같아야 한다.
만일 이 인자가 NULL 이라면 컴퓨터가 스스로 버퍼가 취할 수 있는 최소의 크기를 결정하게 된다.

   리턴값
 

만일 버퍼가 성공적으로 지정 되었다면 0 이 리턴된다.
그렇지 않을 경우 0 이 아닌 값이 리턴되는데 보통 적당하지 않은 size 값을 인자로 넘겼거나, size 에 NULL 을 넘겼지만 컴퓨터가 메모리를 할당하는 과정 중 오류가 발생하는 경우이다.

   실행 예제
 

/*

파일을 _IOFBF 형식으로 열며, 버퍼는 크기가 1024 바이트로 컴퓨터가 자동으로 할당하게 한다.
이 예제는 http://www.cplusplus.com/reference/clibrary/cstdio/setvbuf/
에서 가져왔습니다.

 */
#include <stdio.h>
int main ()
{
    FILE *pFile;
    pFile=fopen ("myfile.txt","w");
    setvbuf ( pFile , NULL , _IOFBF , 1024 );

    /* 여러 파일 입출력 작업들 */

    fclose (pFile);

    return 0;
}


스트림 버퍼 형식이 _IOFBF, 즉 fully buffered 스트림이기 때문에 버퍼가 다 차지 않는 이상 쓰기 작업을 하지 않게 된다. 즉, 버퍼에 1024 바이트 이상 쓰이지 않는 이상 myfile.txt 에 쓰이지 않는다.


'Linux > C/C++' 카테고리의 다른 글

nslookup -> getaddrinfo 사용 사례  (0) 2012.07.16
LFS (Large File Support)  (0) 2012.07.11
recvmsg sendmsg example from italy  (0) 2012.07.11
Socket Programming (sendmsg(), recvmsg() example)  (0) 2012.07.11
sendmsg(2) - Linux man page  (0) 2012.07.11

http://www.sikurezza.org/devel/msg00122.html


Archivio: devel@sikurezza.org
Soggetto: Re: recvmsg()
Mittente: Mauro Tortonesi
Data: 19 May 2002 20:49:01 -0000
On Fri, 17 May 2002, Kundera wrote:

> Ciao a tutti, vorrei sapere sta cosa, utilizzando un 
> sok=socket(AF_INET6,SOCK_RAW,IPPROTO_ICMPV6) e la n=recvmsg(sok,&msg,0) 
> l'indirizzo ipv6 del pacchetto in arrivo dove viene messo!?


dalla manpage di recvmsg:

The recvmsg call uses a msghdr structure to minimize the number of 
directly supplied parameters.  This structure has the following form, 
as defined in <sys/socket.h>:

              struct msghdr {
                  void         * msg_name;     /* optional address */
                  socklen_t    msg_namelen;    /* size of address */
                  struct iovec * msg_iov;      /* scatter/gather array */
                  size_t       msg_iovlen;     /* # elements in msg_iov */
                  void         * msg_control;  /* ancillary data, see below */
                  socklen_t    msg_controllen; /* ancillary data buffer len */
                  int          msg_flags;      /* flags on received message */
              };

Here msg_name and msg_namelen specify the source  address  if  the socket  
is  unconnected; msg_name  may  be  given  as a null pointer if no names are 
desired or required.  The fields msg_iov and msg_iovlen describe scatter-gather 
locations, as discussed  in  readv(2). The field  msg_control,  which  has length 
msg_controllen, points to a buffer for other protocol control related messages or 
miscellaneous ancillary data. When recvmsg is  called,  msg_controllen should 
contain the length of the available buffer in msg_control; upon return from a
successful call it will contain the length of the control message sequence.


quindi basta fare:


/* occhio ai jumbo datagrams!!! non e' detto che un buffer di 64kb sia 
 * sufficiente */
const size_t BUFFER_LENGHT = 65536;

int ret;
uint8_t *my_buffer;
struct msghdr msg;
struct iovec iov;
struct sockaddr_storage ss;
struct sockaddr_in6 *sin6;

memset(&msg, 0, sizeof(msg));

my_buffer = (uint8_t *)malloc(BUFFER_LENGTH);
if (my_buffer == NULL) exit(EXIT_FAILURE);

/* qui setti correttamente il buffer di destinazione */
iov.iov_base = my_buffer;
iov.iov_len = BUFFER_LENGHT;

msg.msg_name = &ss;
msg.msg_namelen = sizeof(ss);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

ret = recvmsg(sok, &msg, 0);

/* qui controlli che non ci siano stati errori */
...

sin6 = (struct sockaddr_in6 *)&ss;


e a questo punto ti ritrovi nella struttura sin6 (e piu' precisamente nel 
campo sin6_addr) l'indirizzo ipv6 del mittente. comunque se non vuoi 
allocare un buffer di 128 byte (sizeof(struct sockaddr_storage) ritorna 
128 bytes) solo per ottenere l'indirizzo ipv6 del mittente puoi procedere 
alla estrapolazione dell'indirizzo ipv6 del mittente utilizzando un
altro metodo (molto piu' complesso) previsto dallo rfc2292, che fa uso 
degli "ancillary data objects". vedi piu' sotto.


> ho anche a disposizione l'header ipv6 in arrivo da qualche parte? 

no, la funzione recvmsg _NON_ ti restituisce l'intero pacchetto ipv6
(al contrario di ipv4). dal draft-ietf-ipngwg-rfc2292bis-07.txt (che 
e' destinato a sostituire lo rfc2292) sezione 3, pagina 19:


3.  IPv6 Raw Sockets

   [...]

   Another difference from IPv4 raw sockets is that complete packets
   (that is, IPv6 packets with extension headers) cannot be sent or
   received using the IPv6 raw sockets API.  Instead, ancillary data
   objects are used to transfer the extension headers and hoplimit
   information, as described in Section 6.  Should an application need
   access to the complete IPv6 packet, some other technique, such as the
   datalink interfaces BPF or DLPI, must be used.


pertanto la situazione si complica enormemente. innanzi tutto devi 
dire alla socket di fornirti i dati sul mittente:

int on = 1;
setsockopt(sok, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on));


poi devi preparare un buffer per gli ancillary data objects restituiti da 
recvmsg, e quando ricevi un pacchetto devi andare ad analizzare la struttura 
in6_pktinfo che ti viene restituita seguendo la seguente procedura:


/* 1kb e' anche troppo, ma meglio stare sul sicuro */
const size_t ADO_BUF_SIZE = 1024;
const size_t BUFFER_LENGHT = 65536;

int ret;
uint8_t *ado_buffer;
uint8_t *my_buffer;
struct msghdr msg;
struct iovec iov;
int found = 0;
struct in6_pktinfo info;
struct cmsghdr *cm;
struct in6_addr sender_address;

ado_buffer = (uint8_t *)malloc(ADO_BUF_SIZE);
if (ado_buffer == NULL) exit(EXIT_FAILURE);

my_buffer = (uint8_t *)malloc(BUFFER_LENGTH);
if (my_buffer == NULL) exit(EXIT_FAILURE);

/* qui setti correttamente il buffer di destinazione */
iov.iov_base = my_buffer;
iov.iov_len = BUFFER_LENGHT;

memset(&msg, 0, sizeof(msg));

msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = ado_buffer;
msg.msg_controlen = ADO_BUF_SIZE;

ret = recvmsg(sok, &msg, 0);

/* qui controlli che non ci siano stati errori */
...

for (cm = CMSG_FIRSTHDR(&msg); cm != NULL; cm = CMSG_NXTHDR(&msg, cm)) {
	if (cm->cmsg_level == IPPROTO_IPV6 &&
            cm->cmsg_type == IPV6_PKTINFO &&
            cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
		info = *(struct in6_pktinfo*)CMSG_DATA(cm);
		found = 1;
		break;
	}
}

sender_address = info.ipi6_addr;


spero di essere stato chiaro anche se sono almeno due anni che non uso
le raw socket.

comunque la tua domanda mi sembra un po' off-topic per questa lista. ti 
consiglio di dare un'occhiata al mio sito:

http://project6.ferrara.linux.it

che si occupa esclusivamente di ipv6 e di iscriverti a qualcuna delle 
nostre mailing list (ad esempio project6-it e projec6-devel). 



p.s. scusa la mia insana curiosita', ma che tipo di software stai 
     scrivendo? l'ultima volta che ho lavorato sulle raw socket icmpv6 e' 
     stato quando ho fatto il porting del ping di netkit-base ad ipv6 ;-)


-- 
Aequam memento rebus in arduis servare mentem...

Mauro Tortonesi			mauro@ferrara.linux.it
Ferrara Linux User Group	http://www.ferrara.linux.it
Project6 - IPv6 for Linux	http://project6.ferrara.linux.it


________________________________________________________
http://www.sikurezza.org - Italian Security Mailing List


'Linux > C/C++' 카테고리의 다른 글

LFS (Large File Support)  (0) 2012.07.11
C 언어 레퍼런스 - setvbuf 함수  (0) 2012.07.11
Socket Programming (sendmsg(), recvmsg() example)  (0) 2012.07.11
sendmsg(2) - Linux man page  (0) 2012.07.11
recvmsg(2) - Linux man page  (0) 2012.07.11
   struct msghdr        Msghdr;
   struct iovec         Iov;

   /* ...... */

    Iov.iov_base = (void *)msgcontent;
    Iov.iov_len = sizeof(msgcontent);

    Msghdr.msg_name = &their_addr;
    Msghdr.msg_namelen = sizeof(struct sockaddr_in);
    Msghdr.msg_iov = (struct iovec *) &Iov;
    Msghdr.msg_iovlen = 1;
    Msghdr.msg_control = NULL;
    Msghdr.msg_controllen = 0;
    
    numbytes = sendmsg(sockfd, &Msghdr, 0);



/////////////////////////////////////////////////////////////////////


 char    buf[MAXBUFLEN];
    struct sockaddr_in  their_addr; 
    struct msghdr       Msghdr;
    struct iovec        Iovec;

    Iovec.iov_base = (void *)buf;
    Iovec.iov_len = sizeof(buf);

    Msghdr.msg_name = &their_addr;
    Msghdr.msg_namelen = sizeof(struct sockaddr_in);
    Msghdr.msg_iov = &Iovec;
    Msghdr.msg_iovlen = 1;

    numbytes = recvmsg(sockfd, &Msghdr, 0);


Name

send, sendto, sendmsg - send a message on a socket

Synopsis

#include <sys/types.h>
#include <sys/socket.h>

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
               const struct sockaddr *dest_addr, socklen_t addrlen);

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

Description

The system calls send(), sendto(), and sendmsg() are used to transmit a message to another socket.

The send() call may be used only when the socket is in a connected state (so that the intended recipient is known). The only difference between send() and write(2) is the presence of flags. With a zero flags argument, send() is equivalent to write(2). Also, the following call

send(sockfd, buf, len, flags);

is equivalent to

sendto(sockfd, buf, len, flags, NULL, 0);

The argument sockfd is the file descriptor of the sending socket.

If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments dest_addr and addrlen are ignored (and the error EISCONN may be returned when they are not NULL and 0), and the error ENOTCONN is returned when the socket was not actually connected. Otherwise, the address of the target is given by dest_addr with addrlen specifying its size. For sendmsg(), the address of the target is given by msg.msg_name, with msg.msg_namelen specifying its size.

For send() and sendto(), the message is found in buf and has length len. For sendmsg(), the message is pointed to by the elements of the array msg.msg_iov. The sendmsg() call also allows sending ancillary data (also known as control information).

If the message is too long to pass atomically through the underlying protocol, the error EMSGSIZE is returned, and the message is not transmitted.

No indication of failure to deliver is implicit in a send(). Locally detected errors are indicated by a return value of -1.

When the message does not fit into the send buffer of the socket, send() normally blocks, unless the socket has been placed in nonblocking I/O mode. In nonblocking mode it would fail with the error EAGAIN or EWOULDBLOCK in this case. The select(2) call may be used to determine when it is possible to send more data.

The flags argument is the bitwise OR of zero or more of the following flags.

MSG_CONFIRM (Since Linux 2.3.15)
Tell the link layer that forward progress happened: you got a successful reply from the other side. If the link layer doesn't get this it will regularly reprobe the neighbor (e.g., via a unicast ARP). Only valid on SOCK_DGRAM and SOCK_RAW sockets and currently only implemented for IPv4 and IPv6. See arp(7) for details.
MSG_DONTROUTE
Don't use a gateway to send out the packet, only send to hosts on directly connected networks. This is usually used only by diagnostic or routing programs. This is only defined for protocol families that route; packet sockets don't.
MSG_DONTWAIT (since Linux 2.2)
Enables nonblocking operation; if the operation would block, EAGAIN or EWOULDBLOCK is returned (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)).
MSG_EOR (since Linux 2.2)
Terminates a record (when this notion is supported, as for sockets of type SOCK_SEQPACKET).
MSG_MORE (Since Linux 2.4.4)
The caller has more data to send. This flag is used with TCP sockets to obtain the same effect as the TCP_CORK socket option (see tcp(7)), with the difference that this flag can be set on a per-call basis.

Since Linux 2.6, this flag is also supported for UDP sockets, and informs the kernel to package all of the data sent in calls with this flag set into a single datagram which is only transmitted when a call is performed that does not specify this flag. (See also the UDP_CORK socket option described in udp(7).)

MSG_NOSIGNAL (since Linux 2.2)
Requests not to send SIGPIPE on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned.
MSG_OOB
Sends out-of-band data on sockets that support this notion (e.g., of type SOCK_STREAM); the underlying protocol must also support out-of-band data.
The definition of the msghdr structure follows. See recv(2) and below for an exact description of its fields.
struct msghdr {
    void         *msg_name;       /* optional address */
    socklen_t     msg_namelen;    /* size of address */
    struct iovec *msg_iov;        /* scatter/gather array */
    size_t        msg_iovlen;     /* # elements in msg_iov */
    void         *msg_control;    /* ancillary data, see below */
    size_t        msg_controllen; /* ancillary data buffer len */
    int           msg_flags;      /* flags on received message */
};
You may send control information using the msg_control and msg_controllen members. The maximum control buffer length the kernel can process is limited per socket by the value in /proc/sys/net/core/optmem_max; see socket(7).

Return Value

On success, these calls return the number of characters sent. On error, -1 is returned, and errno is set appropriately.

Errors

These are some standard errors generated by the socket layer. Additional errors may be generated and returned from the underlying protocol modules; see their respective manual pages.

EACCES

(For UNIX domain sockets, which are identified by pathname) Write permission is denied on the destination socket file, or search permission is denied for one of the directories the path prefix. (See path_resolution(7).)

EAGAIN or EWOULDBLOCK
The socket is marked nonblocking and the requested operation would block. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
EBADF

An invalid descriptor was specified.

ECONNRESET
Connection reset by peer.
EDESTADDRREQ
The socket is not connection-mode, and no peer address is set.
EFAULT

An invalid user space address was specified for an argument.

EINTR

A signal occurred before any data was transmitted; see signal(7).

EINVAL

Invalid argument passed.

EISCONN
The connection-mode socket was connected already but a recipient was specified. (Now either this error is returned, or the recipient specification is ignored.)
EMSGSIZE
The socket type requires that message be sent atomically, and the size of the message to be sent made this impossible.
ENOBUFS
The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.)
ENOMEM

No memory available.

ENOTCONN
The socket is not connected, and no target has been given.
ENOTSOCK
The argument sockfd is not a socket.
EOPNOTSUPP
Some bit in the flags argument is inappropriate for the socket type.
EPIPE

The local end has been shut down on a connection oriented socket. In this case the process will also receive a SIGPIPE unless MSG_NOSIGNAL is set.

Conforming To

4.4BSD, SVr4, POSIX.1-2001. These function calls appeared in 4.2BSD.

POSIX.1-2001 only describes the MSG_OOB and MSG_EOR flags. POSIX.1-2008 adds a specification of MSG_NOSIGNAL. The MSG_CONFIRM flag is a Linux extension.

Notes

The prototypes given above follow the Single UNIX Specification, as glibc2 also does; the flags argument was int in 4.x BSD, but unsigned int in libc4 and libc5; the len argument was int in 4.x BSD and libc4, but size_t in libc5; the addrlen argument was int in 4.x BSD and libc4 and libc5. See also accept(2).

According to POSIX.1-2001, the msg_controllen field of the msghdr structure should be typed as socklen_t, but glibc currently types it as size_t.

Bugs

Linux may return EPIPE instead of ENOTCONN.

Example

An example of the use of sendto() is shown in getaddrinfo(3).

See Also

fcntl(2), getsockopt(2), recv(2), select(2), sendfile(2), shutdown(2), socket(2), write(2), cmsg(3), ip(7), socket(7), tcp(7), udp(7)

Referenced By

ddp(7), ipv6(7), lwres_getaddrinfo(3), netlink(7), perlfunc(1), raw(7), rds(7), rds-rdma(7), recvmmsg(2), sctp(7), socketcall(2), syscalls(2), unix(7)


Name

recv, recvfrom, recvmsg - receive a message from a socket

Synopsis

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

Description

The recvfrom() and recvmsg() calls are used to receive messages from a socket, and may be used to receive data on a socket whether or not it is connection-oriented.

If src_addr is not NULL, and the underlying protocol provides the source address, this source address is filled in. When src_addr is NULL, nothing is filled in; in this case, addrlen is not used, and should also be NULL. The argument addrlen is a value-result argument, which the caller should initialize before the call to the size of the buffer associated with src_addr, and modified on return to indicate the actual size of the source address. The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.

The recv() call is normally used only on a connected socket (see connect(2)) and is identical to recvfrom() with a NULL src_addr argument.

All three routines return the length of the message on successful completion. If a message is too long to fit in the supplied buffer, excess bytes may be discarded depending on the type of socket the message is received from.

If no messages are available at the socket, the receive calls wait for a message to arrive, unless the socket is nonblocking (see fcntl(2)), in which case the value -1 is returned and the external variable errno is set to EAGAIN or EWOULDBLOCK. The receive calls normally return any data available, up to the requested amount, rather than waiting for receipt of the full amount requested.

The select(2) or poll(2) call may be used to determine when more data arrives.

The flags argument to a recv() call is formed by ORing one or more of the following values:

MSG_CMSG_CLOEXEC (recvmsg() only; since Linux 2.6.23)
Set the close-on-exec flag for the file descriptor received via a UNIX domain file descriptor using the SCM_RIGHTS operation (described in unix(7)). This flag is useful for the same reasons as the O_CLOEXEC flag of open(2).
MSG_DONTWAIT (since Linux 2.2)
Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)).
MSG_ERRQUEUE (since Linux 2.2)
This flag specifies that queued errors should be received from the socket error queue. The error is passed in an ancillary message with a type dependent on the protocol (for IPv4 IP_RECVERR). The user should supply a buffer of sufficient size. See cmsg(3) and ip(7) for more information. The payload of the original packet that caused the error is passed as normal data via msg_iovec. The original destination address of the datagram that caused the error is supplied via msg_name.
For local errors, no address is passed (this can be checked with the cmsg_len member of the cmsghdr). For error receives, the MSG_ERRQUEUE is set in the msghdr. After an error has been passed, the pending socket error is regenerated based on the next queued error and will be passed on the next socket operation.

The error is supplied in a sock_extended_err structure:

#define SO_EE_ORIGIN_NONE    0
#define SO_EE_ORIGIN_LOCAL   1
#define SO_EE_ORIGIN_ICMP    2
#define SO_EE_ORIGIN_ICMP6   3

struct sock_extended_err
{
    uint32_t ee_errno;   /* error number */
    uint8_t  ee_origin;  /* where the error originated */
    uint8_t  ee_type;    /* type */
    uint8_t  ee_code;    /* code */
    uint8_t  ee_pad;     /* padding */
    uint32_t ee_info;    /* additional information */
    uint32_t ee_data;    /* other data */
    /* More data may follow */
};

struct sockaddr *SO_EE_OFFENDER(struct sock_extended_err *);
ee_errno contains the errno number of the queued error. ee_origin is the origin code of where the error originated. The other fields are protocol-specific. The macro SOCK_EE_OFFENDER returns a pointer to the address of the network object where the error originated from given a pointer to the ancillary message. If this address is not known, the sa_family member of the sockaddr contains AF_UNSPEC and the other fields of the sockaddr are undefined. The payload of the packet that caused the error is passed as normal data.

For local errors, no address is passed (this can be checked with the cmsg_len member of the cmsghdr). For error receives, the MSG_ERRQUEUE is set in the msghdr. After an error has been passed, the pending socket error is regenerated based on the next queued error and will be passed on the next socket operation.

MSG_OOB
This flag requests receipt of out-of-band data that would not be received in the normal data stream. Some protocols place expedited data at the head of the normal data queue, and thus this flag cannot be used with such protocols.
MSG_PEEK
This flag causes the receive operation to return data from the beginning of the receive queue without removing that data from the queue. Thus, a subsequent receive call will return the same data.
MSG_TRUNC (since Linux 2.2)
For raw (AF_PACKET), Internet datagram (since Linux 2.4.27/2.6.8), and netlink (since Linux 2.6.22) sockets: return the real length of the packet or datagram, even when it was longer than the passed buffer. Not implemented for UNIX domain (unix(7)) sockets.

For use with Internet stream sockets, see tcp(7).

MSG_WAITALL (since Linux 2.2)
This flag requests that the operation block until the full request is satisfied. However, the call may still return less data than requested if a signal is caught, an error or disconnect occurs, or the next data to be received is of a different type than that returned.
The recvmsg() call uses a msghdr structure to minimize the number of directly supplied arguments. This structure is defined as follows in <sys/socket.h>:
struct iovec {                    /* Scatter/gather array items */
    void  *iov_base;              /* Starting address */
    size_t iov_len;               /* Number of bytes to transfer */
};

struct msghdr {
    void         *msg_name;       /* optional address */
    socklen_t     msg_namelen;    /* size of address */
    struct iovec *msg_iov;        /* scatter/gather array */
    size_t        msg_iovlen;     /* # elements in msg_iov */
    void         *msg_control;    /* ancillary data, see below */
    size_t        msg_controllen; /* ancillary data buffer len */
    int           msg_flags;      /* flags on received message */
};
Here msg_name and msg_namelen specify the source address if the socket is unconnected; msg_name may be given as a NULL pointer if no names are desired or required. The fields msg_iov and msg_iovlen describe scatter-gather locations, as discussed in readv(2). The field msg_control, which has length msg_controllen, points to a buffer for other protocol control-related messages or miscellaneous ancillary data. When recvmsg() is called, msg_controllen should contain the length of the available buffer in msg_control; upon return from a successful call it will contain the length of the control message sequence.

The messages are of the form:

struct cmsghdr {
    socklen_t     cmsg_len;     /* data byte count, including hdr */
    int           cmsg_level;   /* originating protocol */
    int           cmsg_type;    /* protocol-specific type */
/* followed by
    unsigned char cmsg_data[]; */
};
Ancillary data should only be accessed by the macros defined in cmsg(3).

As an example, Linux uses this ancillary data mechanism to pass extended errors, IP options, or file descriptors over UNIX domain sockets.

The msg_flags field in the msghdr is set on return of recvmsg(). It can contain several flags:

MSG_EOR
indicates end-of-record; the data returned completed a record (generally used with sockets of type SOCK_SEQPACKET).
MSG_TRUNC
indicates that the trailing portion of a datagram was discarded because the datagram was larger than the buffer supplied.
MSG_CTRUNC
indicates that some control data were discarded due to lack of space in the buffer for ancillary data.
MSG_OOB
is returned to indicate that expedited or out-of-band data were received.
MSG_ERRQUEUE
indicates that no data was received but an extended error from the socket error queue.

Return Value

These calls return the number of bytes received, or -1 if an error occurred. The return value will be 0 when the peer has performed an orderly shutdown.

Errors

These are some standard errors generated by the socket layer. Additional errors may be generated and returned from the underlying protocol modules; see their manual pages.

EAGAIN or EWOULDBLOCK
The socket is marked nonblocking and the receive operation would block, or a receive timeout had been set and the timeout expired before data was received. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.
EBADF

The argument sockfd is an invalid descriptor.

ECONNREFUSED
A remote host refused to allow the network connection (typically because it is not running the requested service).
EFAULT

The receive buffer pointer(s) point outside the process's address space.

EINTR

The receive was interrupted by delivery of a signal before any data were available; see signal(7).

EINVAL

Invalid argument passed.

ENOMEM

Could not allocate memory for recvmsg().

ENOTCONN
The socket is associated with a connection-oriented protocol and has not been connected (see connect(2) and accept(2)).
ENOTSOCK
The argument sockfd does not refer to a socket.

Conforming To

4.4BSD (these function calls first appeared in 4.2BSD), POSIX.1-2001.

POSIX.1-2001 only describes the MSG_OOB, MSG_PEEK, and MSG_WAITALL flags.

Notes

The prototypes given above follow glibc2. The Single UNIX Specification agrees, except that it has return values of type ssize_t (while 4.x BSD and libc4 and libc5 all have int). The flags argument is int in 4.x BSD, but unsigned int in libc4 and libc5. The len argument is int in 4.x BSD, but size_t in libc4 and libc5. The addrlen argument is int * in 4.x BSD, libc4 and libc5. The present socklen_t * was invented by POSIX. See also accept(2).

According to POSIX.1-2001, the msg_controllen field of the msghdr structure should be typed as socklen_t, but glibc currently types it as size_t.

See recvmmsg(2) for information about a Linux-specific system call that can be used to receive multiple datagrams in a single call.

Example

An example of the use of recvfrom() is shown in getaddrinfo(3).

See Also

fcntl(2), getsockopt(2), read(2), recvmmsg(2), select(2), shutdown(2), socket(2), cmsg(3), sockatmark(3), socket(7)

Referenced By

ddp(7), getifaddrs(3), netlink(7), packet(7), raw(7), rds(7), rds-rdma(7), sctp(7), socketcall(2), syscalls(2), udp(7)