64位系统上的老程序

之前写的某小程序在新的64位系统上seg fault了……
gdb之后,发现是getenv()返回了一个很小的负数…… 但是getenv()返回类型是char*,应该或者返回地址或者返回NULL啊……
编译的时候有个warning,说将int转化为了pointer……

其实就是忘了#include 了…… 于是getenv()没有声明过,默认成了返回int
32位系统上无所谓,反正int和char*一样大。64位就不一样了,int只有32位。
于是,在没有声明的情况下,返回值只拿到了后32位…… 再赋给char*自然也不行。

所以c这个没有声明直接可以用的特性有什么好的,而且连个没声明的警告也没有(貌似新的gcc会给……),不如直接编译失败……

尝试训练Gmail垃圾邮件过滤器

起因是我VPS发出来的邮件被扔到 Spam 里去了……
首先根据之前经验,观察邮件的原文里的 Google 的 SPF 的评价,标记为 neutral。
为了试图将其往更好的方向转变,首先我跑去 GoDaddy 在域名信息里搞上了SPF,顺便去 PhotonVPS 开了个 ticket 让他们设好Reverse DNS。SPF用于说明这个域名允许哪些IP以他的名义发邮件,而Reverse DNS用于IP到域名的反查,如果发送者IP的反查结果能和发送者邮箱域名部分匹配上,对于垃圾邮件过滤识别结果也有帮助。这俩倒挺快,GoDaddy 那个完全是程序完成,PhotonVPS 那个 ticket 也是一会儿就有人搞定了。
再发一封邮件试试,嗯,SPF 评价为 pass 了。但是,这封邮件还是跑 Spam 去了……
Google 的理由依旧是这个邮件很像垃圾邮件,跑去看帮助,大约也就是啥你这IP好像发垃圾邮件啦,你这内容像垃圾邮件啦之类。我服务器就发发自检报告,又不骚扰别人,有啥像垃圾邮件的……

既然这样么,只好试试训练他了。GMail 有个”Not Spam”按钮,大约是能够训练垃圾邮件分类器,告诉他这个不是垃圾。
尝试性的一封一封邮件发,一封一封标记”Not Spam”… 过了大约五六封,还真不扔到Spam里去了……
再发几个,也是这样。

刚才还收到了自检报告,看来基本上算训练成了。但是不清楚这个训练对发到别的邮箱的邮件有没有用,其实整个事情起因就是发到贾叔邮箱的确认邮件进了Spam导致他找不到……
当然,如果只关心自己的邮箱,那貌似把发送者加为联系人就成了。但是我不是这个情况……

PS: 后来尝试给贾叔发了个邮件,结果这封邮件没有进Spam,看来对别人的邮箱也是有用的。

关于ICS/JB下命令行启动程序segfault的问题

其实很多人研究过这个问题了,只是最近我碰到这次原因有些不同……
症状是在命令行执行命令碰到segmentation fault,logcat里有:

W/dalvikvm(12364): Exception Ljava/lang/NullPointerException; thrown while initializing Ljava/lang/System;
W/dalvikvm(12364): Exception Ljava/lang/ExceptionInInitializerError; thrown while initializing Ljava/lang/ClassLoader$SystemClassLoader;
....
I/dalvikvm(12364): java.lang.ExceptionInInitializerError:
I/dalvikvm(12364):     at java.lang.ClassLoader.getSystemClassLoader(ClassLoader.java:125)
I/dalvikvm(12364):     at dalvik.system.NativeStart.main(Native Method)
I/dalvikvm(12364): Caused by:
I/dalvikvm(12364): java.lang.ExceptionInInitializerError:
I/dalvikvm(12364):     at java.lang.ClassLoader.createSystemClassLoader(ClassLoader.java:100)
....
I/dalvikvm(12364): Caused by:
I/dalvikvm(12364): java.lang.NullPointerException:
I/dalvikvm(12364):     at java.util.Hashtable.put(Hashtable.java:365)
I/dalvikvm(12364):     at java.lang.System.initSystemProperties(System.java:286)
I/dalvikvm(12364):     at java.lang.System.getProperties(System.java:260)
....

原因大致上就是LD_LIBRARY_PATH里少了东西。ICS以后的系统,启动Android那套VM需要LD_LIBRARY_PATH里必须要有一些路径,默认是/vendor/lib和/system/lib,试下来至少要/system/lib,否则会找不到库,然后…… 不清楚之后发生了什么,反正最后就导致上面那个错误……

但是默认情况是有的,啥时候没有呢?其实这次碰见bug是因为,我为了弄到root把shell(/system/bin/mksh) setuid了…… 然后在shell里就一直有问题……
对于setuid的程序,启动时effective uid与uid不符,动态链接器便会调用linker_secure_env(),把LD_LIBRARY_PATH从环境里踢掉。ICS之后的系统又必须要这个值VM才能启动,于是就挂了……
其实这是个古老的安全机制,否则你LD_LIBRARY_PATH指到别的地方启动setuid程序,就能让他加载你的库,然后以root权限调用到你的库里去,那自然想干啥都行……

把sh的权限弄对之后就没事了……

Xoom更新4.1.1

之前听说Android 4.1.1在Xoom上公开测试了,昨儿听说已经OTA了,于是去系统设置里检查更新,没有……
去网上看,说可以清除Google Service Framework的数据再试。试了一次,果然刷出来一个更新,4.1.1的。Google这是故意的么……
下完之后,更新,刚过百分之十几,就成了个倒了的Android的样子,上面还有个红色三角。

根据经验,反正是升级败了。重启,去/cache,发现recovery目录里有last_log。看最后说啥验证失败。

E: failed to verify whole-file signature
E: signature verification failed
Installation aborted

我一开始以为是更新包验证败了,但是弄到电脑上,能正常解开……(更新包就在/cache里,一个zip) 那估计和上次一样,因为这是个补丁包,所以要先验证源文件正确,才能打补丁,而某些文件可能被改过了,在Root或者别的时候。

这时候我想起来,上次见到过一个叫OTA RootKeeper的东西,可以在更新后保留Root,就去装了一个,打开之后,选保留Root以及暂时unroot。
然后重启到EOS Recovery,尝试装那个zip,果然有apply_patch_check失败的,首先是/system/bin/gzip。进去一看,貌似被改成了到busybox的链接…… 大约是哪个busybox安装器干的好事。从网上下了一个4.0.4的完整镜像,从里面扒出gzip换掉系统里那个符号链接,再试,这次换成了ip。重复操作,搞定了ping和toolbox之后,这次是boot分区校验和挂了…… 重启到fastboot,刷那个4.0.4镜像里的,再更新,还是不行……

想想大概是4.0.4那个镜像的boot改过了,去下载的地方,发现是root过的镜像…… 搜了一圈,就是找不到没root过的4.0.4 boot分区。
网上的恢复办法都是刷回3.x然后一路更新上来,好像很麻烦…… 我想,可以弄来3.x的boot.img和之后所有的补丁,一路打上来,弄到一个4.0.4的boot.img,再刷进去……

在xda的http://forum.xda-developers.com/showthread.php?t=1597609这个帖子里有个网站,里面有3.0.1的原始镜像和所有升级包。把镜像里的boot.img和升级包里的所有boot.img.p扒出来,然后就是咋打补丁的问题了。
我记得android里有applypatch,于是去android source里面找,果然有这个东西。尝试编译一份主机可用的,居然成功了,命令如下:
在applypatch目录里

gcc utils.c imgpatch.c bspatch.c freecache.c bsdiff.c main.c applypatch.c -o applypatch -I../../../system/core/include/ -I../ ../minelf/Retouch.c -lbz2 -lz ../../bootloader/legacy/libc/sha.c ../mtdutils/mtdutils.c

然后跑,发现命令格式挺简单,就是

applypatch 源文件 目标文件 目标sha1 目标大小 源文件sha1:补丁

话说居然要目标sha1…… 用来验证是不是打对了么……
sha1嘛,可以从各个补丁包里的updater-script脚本里发现,就是脚本里调apply_patch()的参数。

用这个办法搞定4.0.4的boot.img,确定sha1没问题后用fastboot刷了进去。然后再更新,终于成功了……
再开OTA RootKeeper,恢复Root,于是Root也有了…… 真方便……

关于4.1的感想:貌似是流畅了点,另外可以玩Google Now了~