2005 年 3 月
循序渐进替换 libcrypto.aprintf 中的 %* 格式strip 静态库openssl 静态库连接顺序关 于静态库的连接顺序不易觉察的类 型提升The Blower's Daughtergcc -MMopenssl 里的 stack API 的问题openssl ca -extfile 参数
循序渐进替换libcrypto.a
日有所得 2005-03-01 09:24:45
将编译过的 openssl-0.9.7e/apps/*.o 打包成 libappobjs.a,将 libcrypto.a 和 libappobjs.a 放入自己的目录,libssl 就用最新安装到 /usr/lib/ 中的库就行了,新改和新增的源文件编译成 .o 之后添加或者替换到 libcrypto.a 中去,链结就很简单了:
CRYPTO_OBJS = ../crypto/x509v3/ipextn.o ../crypto/x509v3/v3_lib.o ../crypto/x509v3/v3_prn.o
lib/libcrypto.a : $(CRYPTO_OBJS)
ar r lib/libcrypto.a $^
openssl : lib/libcrypto.a lib/libappobjs.a
$(CC) -o $@ lib/libappobjs.a lib/libcrypto.a -lssl -ldl
printf 中的 %* 格式
日有所得 2005-03-01 17:17:37
今天折腾 OpenSSL 源码遇到的,manual 半天也看不懂,问 hcz 他也不知道,还是写了几行代码试了一下才搞明白:
%* 要对应两个参数,第一个是域宽,第二个是实际要输出的数据,例如:
%*s, width, aString 表示输出字符串 aString,占 width 列
%*d, width, num 表示输出整数 num,占 width 列
举个例子:
int count = 1, indent = 4;
printf("%*sHello world, %d\n", indent, "", count);
将缩进 4 格然后输出 Hello world, 1。因为这里 "" 占四格。这个在调整对齐格式时很有用,在 openssl 的 BIO_printf 之类调用里面大量用到。
想到在华诺打工时他们的 debug_log 函数输出很漂亮,估计是用一个 indent 值来记录竖线的缩进位置,每进入一次函数就 indent++,出来前就 indent--,用的 printf 类函数可能也是要用 %*。
| function foo start
| | function bar start
| | ...
| | function bar end
| function foo end
strip 静态库
日有所得 2005-03-01 21:02:07
静态库太大了,因为弄过来的 .o 文件都是带有调试信息的,这些现在已经用不着了,也不想重新编译了,strip 一下即可由十几 M 变成几百 K,回到学校时update 也快得多。注意单纯 strip 会把所有符号都去掉了,导致不能连结,strip -d(相当于 strip --strip-debug)就可以了,还有个--strip-unneeded 应该还能更小,不懂连接过程,没有去试。可以直接对静态库进行 strip,可笑我傻乎乎把静态库解开,再 strip,再重新打包 :D
另:注意 cvs update 不会将其他地方加的目录同步,需要 cvs update -d,可加到 ~/.cvsrc 中。
openssl 静态库连接顺序
日有所得 2005-03-10 21:57:43
在 apps/ 目录下连接 openssl :
gcc -o openssl {object files in apps/} ../libssl.a ../libcrypto.a -ldllibssl 一定要在 libcrypto 前面,自己连接改过的代码时,无意中把 libssl 放到 libcrypto 后面去了,结果竟然报一堆错误,折腾好久才发现这个连接顺序有玄机。而且三部分的顺序还都不能改变,玄机是什么呢?为什么顺序变了就不行?明天找人问问。
关于静态库的连接顺序
日有所得 2005-03-11 16:39:57
解决了昨天的疑问,顺序的确是至关重要的。
假设 main.c 中有 main(),依次调用 test.c 中的 test() 和 quit.c 中的 quit()。将他们都编译成 .o,再把 test.o 用 ar 打包成 libtest.a,把quit.o 打包成 libquit.a,这里为了简单,只用一个目标文件做静态库,但已经足够能说明问题了。
/* main.c */
void test();
void quit();
int main() { test(); quit(); return 0; }
/* test.c */
void test() { }
/* quit.c */
void quit() { }
先编译:
$ gcc -c main.c test.c quit.c
可以看到用目标文件的时候顺序是没有关系的,还有三种组合有兴趣自己试:
$ gcc -o main test.o quit.o main.o
$ gcc -o main quit.o test.o main.o
$ gcc -o main main.o quit.o test.o
打包:
$ ar rc libtest.a test.o
$ ar rc libquit.a quit.o
main.o 在最前面则没有问题:
$ gcc -o main main.o libtest.a libquit.a
其他情况就不行了:
$ gcc -o main libtest.a main.o libquit.a
main.o(.text+0x11): In function `main':
: undefined reference to `test'
collect2: ld returned 1 exit status
$ gcc -o main libquit.a main.o libtest.a
main.o(.text+0x16): In function `main':
: undefined reference to `quit'
collect2: ld returned 1 exit status
$ gcc -o main libquit.a libtest.a main.o
main.o(.text+0x11): In function `main':
: undefined reference to `test'
main.o(.text+0x16): In function `main':
: undefined reference to `quit'
collect2: ld returned 1 exit status
原因是 gcc 连接静态库比较奇怪(不知道为什么),已扫描过的符号表中没有谁引用过当前库中的函数的话,就直接把该函数对应的信息丢掉了,就是说,当前处理的静态库扫 描完了之后,后续的目标代码再来调用刚才库中的函数就找不到了。比如刚才第一种组合,libtest.a 扫描过之后,因为前面没有引用 test() 的,就把 test() 符号信息丢弃,之后连接到 main.o 就找不到 test()了;第二种组合类似;第三种组合 main.o 在最后,于是前面库中的 test() 和 quit() 就都找不到了。
因此昨天连接 openssl 的问题便是 libssl 引用了 libcrypto 中的函数,反之则不是。所以 libssl 一定要位于 libcrypto 前面。
不易觉察的类型提升
日有所得 2005-03-16 20:52:19
(v4range->max[i] | ~mask) != v4range->max[i]
max[i] 和 mask 都是 unsigned char,mask 为 0xff 取反时发生了类型提升,变成了一个负的 int 型整数,bitor 之后两边作为 int 来比较,条件判断意外的为真了。将 ~mask 前面再加上个 (unsigned char) 强制转换才行。要记牢。
mask &= ~(1 << j);
这里中间其实也发生了类型提升,只不过最后又截断为 unsigned char 了。
终于整出来一个切换 hlsearch 的热键了
日有所得 2005-03-17 14:52:48
终于整出来一个切换 hlsearch 的热键了
好好看了一下 vim 的帮助文件 eval.txt,学到了 if-else-endif 的表达式,弄出了一个 F8 热键来切换 hlsearch:
map <F8> :if &hls<CR>set nohls<CR>else<CR>set hls<CR>endif<CR><CR>
最后那个 <CR> 是因为命令执行完还需要敲个键。&option 这个可以访问设置的选项。又如:
if &bg == "light"
hi Comment ctermfg=6 guifg=darkcyan
else
hi Comment ctermfg=3 guifg=darkcyan
endif
另外脚本里面行末写注释时可以用 `|' 指定命令结束了,注释开始了
map <F1> I/* <ESC>A */<ESC>| " Use /* .. */ to comment current line
map <F2> ^xxx$xxx| " Clear /* .. */ comment of current line
The Blower's Daughter
胡思乱想 2005-03-20 20:17:29
(实验室又要搬家了,不知道到我们毕业时它还存不存在。老庞,保重。)
今天看了个电影《Closer(偷心)》,关于爱情,伦理,道德,不会写评论,但确实很触动,配的那首歌非常好,很搭配,很感伤,现在知道名叫 The Blower's Daughter,演唱者 Damien Rice 的嗓音很悲,但并不是个老男人,而是个爱尔兰英俊小生。这首歌这里有个链接:The Blower's Daughter
The Blower's Daughter
Artist: Damien Rice
And so it is
Just like you said it would be
Life goes easy on me
Most of the time
And so it is
The shorter story
No love, no glory
No hero in her sky
I can't take my eyes off of you
I can't take my eyes off you
I can't take my eyes off of you
I can't take my eyes off you
I can't take my eyes off you
I can't take my eyes...
And so it is
Just like you said it should be
We'll both forget the breeze
Most of the time
And so it is
The colder water
The blower's daughter
The pupil in denial
I can't take my eyes off of you
I can't take my eyes off you
I can't take my eyes off of you
I can't take my eyes off you
I can't take my eyes off you
I can't take my eyes...
Did I say that I loathe you?
Did I say that I want to
Leave it all behind?
I can't take my mind off of you
I can't take my mind off you
I can't take my mind off of you
I can't take my mind off you
I can't take my mind off you
I can't take my mind...
My mind...my mind...
'Til I find somebody new
gcc -MM
日有所得 2005-03-28 11:16:41
为了安全、省事,把 c 文件定义为依赖于所有的头文件,现在发现文件多了之后编译不相干的文件浪费了很多时间,现在知道了:
gcc -MM foo.c
这个命令能生成依赖规则,避免改一个头文件重新编译了不相干的 c 文件:
foo.o: foo.c foo.h bar.h
openssl 里的 stack API 的问题
日有所得 2005-03-28 12:34:59
sk_dup() 复制完发现释放了原来的 stack 之后新的 stack 中 data[] 指向的数据变成了垃圾。查了好久发现它的 sk_dup() 根本不复制 data[] 里面的指针指向的内存(因为根本不知道该分配多少),只是 memcpy 一下了事:
openssl-0.9.7e/crypto/stack/stack.c
103 memcpy(ret->data,sk->data,sizeof(char *)*sk->num);
自己修改了一下,写了一个 sk_dup_fn() 函数,要带两个函数指针作参数,一个用于复制 data[i] 中指针指向的内存,另一个用于失败时释放,把上面这条memcpy 改成:
for (i = 0; i < sk->num; i++) {
ret->data[i] = (*dup_fn)(sk->data[i]);
/* avoid null pointer */
if (NULL == ret->data[i]) goto err;
}
...
err:
if (ret != NULL) sk_pop_free(ret, free_fn);(嗯,接触 openssl 之后也用上了 goto,哪个说 goto 影响可读性的,我看 openssl 里面用的都挺好,不用的话又难写又难读 :-)
函数原型:
STACK *sk_dup_fn(const STACK *sk, char* (*dup_fn)(const char *),
void (*free_fn)(void *));
注意,sk_free() 也不是随便用的,它不会释放 data[] 里面指针指向的内存,要用 sk_pop_free() 带一个附加的函数指针做参数。
openssl ca -extfile 参数
日有所得 2005-03-30 17:02:55
可以指定读取扩展的文件名和段名:
-extfile filename
-extfile filename -extensions section
如果不带 -extensions section 就取 [ default ] 段。
下面是包含典型的 CA 证书扩展和普通证书扩展对应的 ext 文件格式:
[ ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
basicConstraints = CA:true
# total blocks: 2
# block 0:
# addressFamily: IPv4, unicast
# ipAddreessChoice: NULL (inherit from issuer)
# block 1:
# addressFamily: IPv6
# ipAddreessChoice: NULL (inherit from issuer)
sbqp-ipAddrBlock = critical,DER:30:11:30:07:04:03:00:01:01:05:...
[ user ]
basicConstraints = CA:FALSE
nsComment = "OpenSSL Generated Certificate"
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
# 拿新写的 ipextn-gen 生成
sbqp-ipAddrBlock = critical,DER:30:49:30:1A:04:03:00:01:01:30:...
一个完整的用法例子:
$ openssl ca -config democa.conf -days 7300 -notext \
-keyfile private/subca.key -cert subca.pem \
-extfile ipextn.ext -extensions user \
-out ipextn.pem -infiles ipextn.req