undefined reference

在很久很久以前,只有GNU ld,后来Google贡献了一个gold,就有了两个linker。

Android 2.3里面的gcc 4.4.3默认搭配的是gold。

$ cd prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/arm-eabi/bin
$ ls -l ld*
-rwxr-xr-x 1 liang liang 32272032 2010-12-20 02:44 ld
-rwxr-xr-x 1 liang liang  3248778 2010-12-20 02:44 ld.bfd
-rwxr-xr-x 1 liang liang 32272032 2010-12-20 02:44 ld.gold

使用gold没有任何问题,但是如果把上面的ld换成ld.bfd,那么在链接system/media/opensles/tests下面的程序时,就会报如下错误:

out/target/product/generic/obj/lib/libOpenSLES.so: undefined reference to `typeinfo for android::SortedVectorImpl'
out/target/product/generic/obj/lib/libOpenSLES.so: undefined reference to `vtable for __cxxabiv1::__vmi_class_type_info'

这说明gold和GNU ld的行为不完全一致。使用linker选项--allow-shlib-undefined可以忽略这个错误。ld手册上说:这个选项会使ld忽略so库内的未定义符号,但是对于命令行上其它.o文件内的未定义符号仍然报错。

因此,可以在相应的Android.mk文件里添加

LOCAL_LDFLAGS := -Wl,--allow-shlib-undefined

来解决这个问题。

尽管使用这个解决方法可以把影响减少到最小,但是因为要修改的地方较多,比较麻烦。最省事的方式是全局使用-fno-rtti选项,或者像某些帖子上建议的那样,把IAndroidEffect.c改名为IAndroidEffect.cpp,并在Android.mk里做相应修改。

说实话,我感觉opensles里面的东西有点怪,明明是C++程序,非要使用后缀名.c,然后编译的时候使用-x c++来当作C++程序编译。问题是这样编译就没有使用-fno-rtti,因此输出了rtti信息。当然Android的构建系统也不尽如人意,为啥要根据文件后缀名是.c还是.cpp来选择是否使用-fno-rtti呢?

Emacs find file后续

Emacs find file utility的最后,我声称“wl-find-file-in-other-root是functional的”,惭愧啊,偌大的一个find-file-existing在里面调用着,还敢声称是functional的。

知耻后勇,于是重写了部分实现,并且重新设计了接口,无需为每个root-list新写一个调用命令。代码如下:

(defvar wl-find-file-gcc-root-list nil)
(defvar wl-find-file-binutils-root-list nil)
(defvar wl-find-file-completing-read-function
  'wl-iswitchb-completing-read)

(defun wl-iswitchb-completing-read (prompt choices
                                    &optional dummy require-match)
  "Use iswitchb completion functionality."
  (let ((iswitchb-make-buflist-hook
         (lambda ()
           (setq iswitchb-temp-buflist
                 (cond ((consp (car choices))
                        (mapcar 'car choices))
                       ((stringp (car choices))
                        choices)
                       ((symbolp (car choices))
                        (mapcar 'symbol-name choices))
                       (t
                        (error "Unknown type of choices.")))))))
    (iswitchb-read-buffer prompt nil require-match)))

(defun wl-find-file-root (filename root-list)
  (find-if (lambda (dir)
             (when (> (length filename)
                      (length dir))
               (string-equal dir (substring filename
                                            0 (length dir)))))
           root-list))

(defun wl-find-file-other-root-list (filename root-list-list)
  (when root-list-list
    (let ((root (wl-find-file-root filename (car root-list-list))))
      (if root
          (list root (car root-list-list))
        (wl-find-file-other-root-list filename (cdr root-list-list))))))

(defun wl-find-other-file-1 (filename &rest root-list-list)
  (let* ((root (wl-find-file-other-root-list filename root-list-list))
         (relative-path (substring filename (length (car root)))))
    (remove-if (lambda (file)
                 (not (file-exists-p file)))
               (mapcar (lambda (dir)
                         (expand-file-name relative-path dir))
                       (remove-if (lambda (elem)
                                    (string-equal elem (car root)))
                                  (cadr root))))))

(defun wl-find-other-file-other-window (filename)
  (interactive
   (list
    (let ((completion-ignore-case t)
          (file-list (wl-find-other-file-1 (buffer-file-name)
                                           wl-find-file-gcc-root-list
                                           wl-find-file-binutils-root-list)))
      (if file-list
          (funcall wl-find-file-completing-read-function
                   "Open file: "
                   file-list)
        (error "%s does not exist in other directories"
               (file-name-nondirectory (buffer-file-name)))))))
  (find-file-other-window filename))