Monthly Archives: May 2017

记两个gcc的bug

一般来说程序的编译、运行遇到问题,只会怀疑code有问题,而不会怀疑是编译器的问题。不过日子久了,总会遇到各种各样奇怪的问题,最近遇到了两个gcc相关的bug,记录一下。

问题一

在编译一个并不复杂的文件的时候,gcc报内存不够的错误。
这个文件只是定义了一个嵌套了几层的std::map,而且,如果用GCC 4.8来编译,很快就编译完了,占的内存也不多。
然而,如果用GCC 6.2 (或者最新的7.1)来编译,会发现占用的时间非常长,而且占用内存特别多,最终(如果内存不够)会报internal compiler error: Killed的错。
观察发现,在编译的过程中gcc占用的内存越来越多,分配了足够多的swap之后能编译成功,最终内存要占到16~20GiB左右。
同事去submit了一个bug,https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80290,被confirm了。
不过,从comments看,似乎还挺复杂的,什么时候能修好,就难说了。。。
另外,用clang的话也不会出现这个bug。

问题二

在一个动态库里,遇到一个std::unique_ptr相关的segment fault。这个变量在constructor里明明创建过了,但是运行时发现它还是nullptr
简化一下问题,如果一个动态库里有类似这样的code:

std::unique_ptr<MyStruct> s = nullptr;
void onLoad() __attribute__((constructor));
void onLoad()
{
  s = std::make_unique<MyStruct>();
}

当这个动态库被dlopen的时候,我期望s会被onLoad()初始化好,然而在别的函数里用这个s的时候,会segment fault,因为s里面是个空指针!
关于这个问题,我在SO上提了个问题,结果发现,跟这个GCC的这个bug有关:

简单的说,动态库里 constructor 和 global/static 变量的初始化顺序是unspecified,所以有可能onLoad()先执行,然后再初始化global变量s, 把它初始化成nullptr。注意这个时候只是初始化,原来的变量并不会被析构,其实也是内存泄露。
不过,这个bug看上去暂时不会被修复,只是会在文档里注明这种情况初始化顺序是不定的。
要修复这个问题,需要给变量指定初始化顺序:

std::unique_ptr<MyStruct> s __attribute__((init_priority(101))) = nullptr;

注意init_priority的值设为101~65534都可以。 0~100是reserved,用了会有warning; 最大值是65535,也是默认值,要是设成65535,就和__attribute__((constructor))一样,顺序就变成unspecified了。
另外,clang也支持这个属性,表现也gcc一样,有同样的bug。

Share

在墙内用gmail两步认证发送kernel patch

Linux Kernel的开发,都是通过邮件发patch的形式来review的。如果用自己的邮件服务器当然没问题。
不过如果想用gmail帐号来发patch,必须翻墙才行;
如果你的gmail帐号开通了两步认证(如果没有,建议开通),还需要额外的App-Specific密码。

这里介绍一个简便的方法。

前提

有一个翻墙的方式,比如Shadowsocks
开通了gmail两步认证。

原理

  • 配置git使用msmtp来发送邮件;
  • 通过proxychains来翻墙;
  • 通过App-Specific密码来登录Google的SMTP server

步骤(以shadowsocks为例)

  1. 安装msmtp, proxychains
    sudo apt-get install msmtp proxychains
    
  2. 配置msmtp使用gmail的SMTP服务
    $ cat ~/.msmtprc
    # Example for a user configuration file
    # Set default values for all following accounts.
    defaults
    tls on
    tls_trust_file /etc/ssl/certs/ca-certificates.crt
    logfile ~/msmtp.log
    # My email service
    account gmail
    host smtp.gmail.com
    port 587
    from <your-gmail-id>@gmail.com
    auth on
    user <your-gmail-id> @gmail.com
    password
    # Set a default account
    account default : gmail
    
  3. 配置proxychains使用shadowsocks
    $ cat /etc/proxychains.conf
    ...
    [ProxyList]
    # add proxy here ...
    socks5 127.0.0.1 1080 ## Set socks5 proxy to your sslocal ip/port
    
  4. 配置App-Specific Password
    1. 登录到https://security.google.com/settings/security/apppasswords
    2. Generate一个密码,建议设置一个custom name,比如说linux-kernel
    3. 这个密码一般是16个ascii码,保留好这个密码,建议保存在KeePass里。
  5. 生成git patch并发送邮件
    # 生成patch
    # 生成patch
    git format-patch --to=<kernel-mailing-list> --cc=<reviewer> --subject-prefix="PATCH linux" <revision range>
    
    # 启动shadowsocks
    sslocal -c <your-ss-config.json>&
    
    # 通过proxychains来发送邮件
    proxychains git send-email <your patches>.patch
    

    当它提示要输密码的时候,不要输gmail的密码,输入之前generate的linux-kernel的密码

    password for <your-gmail-id>@gmail.com at smtp.gmail.com:
    

Done!

Share