27

如何解决编译包含函数 gethostbyname 的代码的静态二进制文件,如果在没有警告的情况下编译,如下所示:

警告:在静态链接的应用程序中使用“gethostbyname”需要在运行时使用 glibc 版本中用于链接的共享库

我使用命令在 ubuntu 12.04 上编译:

$ gcc -static lookup.c -o lookup

这是lookup.c的代码:

  /* lookup.c */

  #include <stdio.h>
  #include <unistd.h>
  #include <stdlib.h>
  #include <string.h>
  #include <errno.h>
  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  #include <netdb.h>

  extern int h_errno;

  int main(int argc,char **argv) {
     int x, x2;
     struct hostent *hp;

     for ( x=1; x<argc; ++x ) {
        hp = gethostbyname(argv[x]);
        if ( !hp ) {
           fprintf(stderr,
                   "%s: host '%s'\n",
                   hstrerror(h_errno),
                   argv[x]);
           continue;
        }

        printf("Host %s : \n" ,argv[x]);
        printf(" Officially:\t%s\n", hp->h_name);
        fputs(" Aliases:\t",stdout);
        for ( x2=0; hp->h_aliases[x2]; ++x2 ) {
           if ( x2 ) {
              fputs(", ",stdout);
             }
        fputs(hp->h_aliases[x2],stdout);
        }     
        fputc('\n',stdout);
        printf(" Type:\t\t%s\n",
               hp->h_addrtype == AF_INET
               ? "AF_INET" : "AF_INET6");
        if ( hp->h_addrtype == AF_INET ) {
           for ( x2=0; hp->h_addr_list[x2]; ++x2 ) {
              printf(" Address:\t%s\n",
                     inet_ntoa( *(struct in_addr *)
                      hp->h_addr_list[x2]));
           }
        }
     putchar('\n');
     }
     return 0;
  }

我想如果我检查通过$ file lookup会得到这样的输出:

查找:ELF 32 位 LSB 可执行文件,Intel 80386,版本 1 (GNU/Linux),静态链接,适用于 GNU/Linux 2.6.24,BuildID[sha1]=0x6fcb2684ad8e5e842036936abb50911cdde47c73,未剥离

不像这样:

查找:ELF 32 位 LSB 可执行文件,Intel 80386,版本 1 (SYSV),动态链接(使用共享库),适用于 GNU/Linux 2.6.24,BuildID[sha1]=0xf9f18671751927bea80de676d207664abfdcf5dc,未剥离

如果你评论了建议我必须使用无静态,因为我知道的每个 linux 都有不同的 libc,我希望你不需要评论。为什么我坚持静态?因为我需要强制使用静态,所以二进制文件必须是静态的而不是动态的。

我有两个多星期的时间来寻找这个,但到目前为止还没有成功。

感谢您帮助我解决我的严重问题。

4

3 回答 3

32

你所要求的将是非常困难的。

请参阅有关 getaddrinfo 的 StackOverflow 问题。基本上,getaddrinfo/下面gethostbyname是 glibc 的 NSS 层。这允许系统管理员说“使用 DNS 将主机名解析为 IP 地址”,或“使用 LDAP”,或“不要使用任何其他东西/etc/hosts”。此控件在运行时;系统管理员可以随时更改主机名解析为 IP 的方式。

由于这种灵活性,glibc 中的所有名称解析调用都使用辅助库(基本上是插件)来完成繁琐的解析工作。有一个用于 LDAP 寻址的共享库,一个用于文件,一个用于 DNS,一个用于 YP,等等。

如果您希望您的程序 100% 静态链接,您将不得不去其他地方(不是gethostbyname)将主机名转换为 IP 地址。您可以使用像uDNS这样的解析器库(不是这个确切的库 - 有类似的工具可用)来做到这一点,但您应该记住,您的二进制文件不会在配置为不使用 DNS 的系统上做正确的事情

相反,我建议只让程序(技术上)动态链接。如果您真的想确保它可以在任何平台上运行,您甚至可以glibc附带二进制文件——尽管这样做需要符合 LGPL。保留这个动态链接仅意味着您不会在glibc版本错误的系统上工作 - 不是一个巨大的兼容性问题。

说到许可证合规性,值得注意的是,如果您静态链接glibc,您很可能必须为您的整个应用程序提供源代码以符合glibcLGPL 许可证。我不是律师,这不是合格的法律建议,但阅读 LGPL 可以清楚地表明,静态链接的应用程序glibc必须是开源的。请参阅有关该主题的 StackOverflow 问题

于 2013-03-01T19:33:01.057 回答
13

我得到了同样的警告,为了修复它,我重新编译了 glibc。配置时打开开关--enable-static-nss以使其工作。

于 2014-02-18T15:22:32.243 回答
0

我有2个答案-

  1. 保持程序的主要部分静态链接,并分离出一个函数程序来调用 gethostbyname()。允许后者动态链接。使用 fork 然后 exec 执行这个单独的程序来获取域名的地址。您可以使用 system() 而不是 fork then exec,尽管它需要更长的时间(整毫秒),这不应该引起关注,因为无论如何您都是在互联网上搜索名称服务器,这需要一些时间。

  2. 编写源代码来做DNS,就像我做的那样。将其编译为存档 (.a) 并在静态链接中进行搜索。

于 2014-01-26T02:01:10.103 回答