티스토리 뷰

개발/Linux

glibc의 iconv를 아시나요?

Jaeyeon Baek 2017.05.18 09:50

iconv 개론

C/C++ 기준으로 개발을 하다보면 문자열을 특정 케릭터셋으로 컨버팅 해야하는 경우가 발생하는데 이때 주로 사용되는 library에는 libiconv라는 것이 있다. 

사용법은 무척 간단한데 우선 오픈소스인 libiconv를 다운받고 컴파일 하고나서 링킹을 걸어 사용하면 되는데 여기서 이런 소소한 것들은 우선 스킵하도록 한다. 어렴풋한 기억으로는 소스는 단순히 configuremake만 돌려도 사용할 수 있다. dynamic library는 기본으로 생성되고, static libraryconfigure에 옵션을 줘야 한다는 점을 기억하자. 마침 이러한 내용은 대부분 아래 링크에서 확인할 수 있다. 

http://www.gnu.org/software/libiconv/

이렇게 컴파일해서 생성한 dynamic library(libiconv.so)는 다른 코드를 gcc로 컴파일 할 때 링킹해서 사용하면 되겠다. 대략 아래와 같다. (이야기의 요지와 관계없는 중간 옵션은 다 생략)

$ gcc test.c -liconv 


test.c에서 iconv를 사용하는 예제는 man 페이지나 인터넷에 널리고 널렸으니 생략하도록 한다. 여튼 이제부터가 중요하다. 보통은 열이면 열, 이러한 방식으로 코딩하고 사용했지만 사실 꼭 위에 예제처럼 libiconv.so를 링킹해서 사용하지 않아도 괜찮다. 무슨소리냐고? 링킹을 사용하지 않아도 대부분의 경우에는 문제 없이 컴파일이 되고 사용이 가능하다. 

libiconv.so를 링킹하지 않고도 사용 가능한 이유를 살펴보면 서버의 상황에 따라 gcc로 컴파일 될 때 기본적으로 링킹되는 glibc가 있는데 여기에도 iconv 관련 함수가 내장되어 있기 때문이다. printf 따위의 standard 함수를 stdio.hinclude하는 것만으로도 사용할 수 있는 이유가 컴파일 과정에서 glibc가 자동으로 링킹되기 때문인데, iconv도 마찬가지로 iconv.hinclude해주면 바로 사용할 수 있다.

$ ldd a.out 
linux-vdso.so.1 =>  (0x00007fff8f5ff000)
libc.so.6 => /lib64/libc.so.6 (0x0000003872800000)
/lib64/ld-linux-x86-64.so.2 (0x0000003872400000)




아래 링크를 참고해보면 glibc 2.1.x부터 iconv가 지원된다고 한다. (코멘터 분들은 꽤 공신력 있는 분들이다.) 정확히 어느 버전부터인지는 크게 중요하지 않으니 굳이 glibc의 히스토리는 따라가니 않도록 하겠다. 

https://kldp.org/comment/358554#comment-358554

두루 살펴보았으니 이제는 libiconv라는 library와 glibc에 있는 iconv(gconv)를 확실히 별도의 모듈로 볼 수 있겠다. 필요하다면 glibc 소스를 다운받아 확인해 보도록 하자. 

https://www.gnu.org/software/libc/sources.html


동작 확인

이제 실제로 test 애플리케이션에서 어떤 iconv가 사용되는지 확인하는 간단한 예제를 살펴보자. 코드에는 iconv_open(), iconv(), iconv_close() 정도가 사용되었고 컴파일은 아래와 같이 libiconv를 링킹하지 않고 진행했다. 

$ gcc test.c


앞의 글을 읽지 않았다면 iconv관련 함수를 찾을 수 없다는 컴파일 에러가 출력 될 것 같지만 이제는 에러 없이 정상적으로 컴파일 되는 이유를 알 수 있다. 그렇다면 정말 내부적으로 어떻게 iconv(gconv)가 사용 가능한지 strace(실행 프로그램의 stack을 유관으로 확인할 수 있도록 해석해 주는 애플리케이션)로 확인을 해보자.  

$ strace ./a.out

....
open("/usr/lib64/gconv/tls/x86_64/libKSC.so", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/gconv/tls/x86_64", 0x7fff7be62780) = -1 ENOENT (No such file or directory)
open("/usr/lib64/gconv/tls/libKSC.so", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/gconv/tls", 0x7fff7be62780) = -1 ENOENT (No such file or directory)
open("/usr/lib64/gconv/x86_64/libKSC.so", O_RDONLY) = -1 ENOENT (No such file or directory)
stat("/usr/lib64/gconv/x86_64", 0x7fff7be62780) = -1 ENOENT (No such file or directory)
open("/usr/lib64/gconv/libKSC.so", O_RDONLY) = 3

....


결과에서 빨간색으로 마킹된 부분을 보면 gconv 쪽 모듈을 사용하고 있으며 libiconv.so는 사용하지 않는 것을 확인할 수 있다. 그렇다면 libiconv를 컴파일 옵션으로 링킹하면 결과가 어떨까? 결과는 마찬가지로 링킹만 되었을뿐 실제 사용되지는 않는다

$ gcc test.c -liconv


여기까지 읽고나면 의문이 생긴다. 현재까지의 내용을 보면 libiconv의 링킹 여부와 관계없이 glibciconv를 사용하게 되는데 어떻게 하면 libiconv에 내장된 함수를 사용할 수 있을까? 그것은 컴파일 타임에 코드상에서 사용한 iconv.h의 헤더 위치를 지정함으로써 어떤 library를 사용할지 결정할 수 있게 된다.

$ gcc test.c -liconv -I/usr/local/include # 대략 이런 형태


정말 그런지는 strace로 각자 확인해보면 된다. 


결론

여러가지를 살펴보았는데 사실 glibciconv가 포함되었다고 한들 libiconv를 배척할 수는 없다. 특수한 환경에서는 우리는 여전히 libiconv.so가 필요하고(gcc 버전 문제나, 플랫폼의 OS 타입 같은) 또한 glibc에 내장된 모듈의 문제에 대응 하는 것보다 libiconv로 대응 하는 것이 훨씬 더 경제적이다. 일반 서버가 아닌 임베디드 보드의 glibc를 패치해보면 이해가 쉽다. 어쩌면 개발하면서 직면하게 되는 side-effect의 끝판왕을 보게 될 지도. 

여하튼 개발하는 프로그램의 환경에 따라 어떤 iconv를 사용할지 적절하게 선택하면 되겠다. 


gconv

glibc에 포함되어 있는 iconv를 일각에서는 gconv라고 표기하기도 하는데, 이에 대한 정확한 근거는 찾아보기 힘들지만 막상 코드를 열어보면 이유를 알 수 있다. iconv()를 호출할 경우 내부적으로는 모두 gconv 라는 네이밍으로 시작하는 변수와 함수로 wrapping되어 있기 때문이다. (iconv 함수는 내부적으로 다시 __gconv 함수를 호출한다.) 

명칭은 아마도 glibcg를 따왔기 때문에 gconv라고 부르는 것이 아닐까?

댓글
댓글쓰기 폼