使用 GCC 驱动程序时,什么使静态库成为 "incompatible"?
When using the GCC driver, what makes a static lib "incompatible"?
所以我想做的是 Ubuntu 14.04 (x86_64) 我想根据最新发布的 1.1.11 版本设置 musl-libc
这一刻。
我所做的是:
- 为 GCC 安装 multilib 支持:
sudo apt-get --no-install-recommends install gcc-multilib
- 分别为 32 位和 64 位配置库并将它们安装到单独的文件夹中:
CFLAGS=-m32 ./configure --prefix=$HOME/bin/musl-32-bit --disable-shared --target=i386-linux-gnu && make && make install
CFLAGS=-m64 ./configure --prefix=$HOME/bin/musl-64-bit --disable-shared --target=x86_64-linux-gnu
然后为了构建静态 linked premake4
,我在 premake4
:
生成的 Makefile
上像这样调用 GNU make
make -j 8 CC=$HOME/bin/musl-32-bit/bin/musl-gcc ARCH=-m32 LDFLAGS="-v -static" verbose=1
这似乎适用于 linking 步骤,其中包含:
Linking Premake4
$HOME/bin/musl-32-bit/bin/musl-gcc -o bin/release/premake4 intermediate/gmake__/premake.o intermediate/gmake__/os_uuid.o intermediate/gmake__/os_pathsearch.o intermediate/gmake__/os_match.o intermediate/gmake__/os_chdir.o intermediate/gmake__/os_mkdir.o intermediate/gmake__/os_stat.o intermediate/gmake__/os_getversion.o intermediate/gmake__/premake_main.o intermediate/gmake__/os_isdir.o intermediate/gmake__/string_endswith.o intermediate/gmake__/os_isfile.o intermediate/gmake__/scripts.o intermediate/gmake__/path_isabsolute.o intermediate/gmake__/os_rmdir.o intermediate/gmake__/os_getcwd.o intermediate/gmake__/os_is64bit.o intermediate/gmake__/os_copyfile.o intermediate/gmake__/lstate.o intermediate/gmake__/ltable.o intermediate/gmake__/lgc.o intermediate/gmake__/lobject.o intermediate/gmake__/lcode.o intermediate/gmake__/lmathlib.o intermediate/gmake__/lbaselib.o intermediate/gmake__/lmem.o intermediate/gmake__/lfunc.o intermediate/gmake__/lparser.o intermediate/gmake__/ldblib.o intermediate/gmake__/lzio.o intermediate/gmake__/lstrlib.o intermediate/gmake__/lvm.o intermediate/gmake__/lauxlib.o intermediate/gmake__/llex.o intermediate/gmake__/lstring.o intermediate/gmake__/ldump.o intermediate/gmake__/ldebug.o intermediate/gmake__/loadlib.o intermediate/gmake__/lopcodes.o intermediate/gmake__/linit.o intermediate/gmake__/ldo.o intermediate/gmake__/lapi.o intermediate/gmake__/liolib.o intermediate/gmake__/loslib.o intermediate/gmake__/lundump.o intermediate/gmake__/ltm.o intermediate/gmake__/ltablib.o -v -static -L. -s -rdynamic -lm -ldl
Using built-in specs.
Reading specs from $HOME/bin/musl-32-bit/lib/musl-gcc.specs
rename spec cpp_options to old_cpp_options
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-m32' '-o' 'bin/release/premake4' '-v' '-static' '-L.' '-s' '-rdynamic' '-specs=$HOME/bin/musl-32-bit/lib/musl-gcc.specs' '-mtune=generic' '-march=i686'
/usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -export-dynamic -z relro -o bin/release/premake4 -s $HOME/bin/musl-32-bit/lib/crt1.o $HOME/bin/musl-32-bit/lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o -L. -L$HOME/bin/musl-32-bit/lib -L /usr/lib/gcc/x86_64-linux-gnu/4.8/. intermediate/gmake__/premake.o intermediate/gmake__/os_uuid.o intermediate/gmake__/os_pathsearch.o intermediate/gmake__/os_match.o intermediate/gmake__/os_chdir.o intermediate/gmake__/os_mkdir.o intermediate/gmake__/os_stat.o intermediate/gmake__/os_getversion.o intermediate/gmake__/premake_main.o intermediate/gmake__/os_isdir.o intermediate/gmake__/string_endswith.o intermediate/gmake__/os_isfile.o intermediate/gmake__/scripts.o intermediate/gmake__/path_isabsolute.o intermediate/gmake__/os_rmdir.o intermediate/gmake__/os_getcwd.o intermediate/gmake__/os_is64bit.o intermediate/gmake__/os_copyfile.o intermediate/gmake__/lstate.o intermediate/gmake__/ltable.o intermediate/gmake__/lgc.o intermediate/gmake__/lobject.o intermediate/gmake__/lcode.o intermediate/gmake__/lmathlib.o intermediate/gmake__/lbaselib.o intermediate/gmake__/lmem.o intermediate/gmake__/lfunc.o intermediate/gmake__/lparser.o intermediate/gmake__/ldblib.o intermediate/gmake__/lzio.o intermediate/gmake__/lstrlib.o intermediate/gmake__/lvm.o intermediate/gmake__/lauxlib.o intermediate/gmake__/llex.o intermediate/gmake__/lstring.o intermediate/gmake__/ldump.o intermediate/gmake__/ldebug.o intermediate/gmake__/loadlib.o intermediate/gmake__/lopcodes.o intermediate/gmake__/linit.o intermediate/gmake__/ldo.o intermediate/gmake__/lapi.o intermediate/gmake__/liolib.o intermediate/gmake__/loslib.o intermediate/gmake__/lundump.o intermediate/gmake__/ltm.o intermediate/gmake__/ltablib.o -lm -ldl --start-group /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcc_eh.a -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o $HOME/bin/musl-32-bit/lib/crtn.o
/usr/bin/ld: skipping incompatible $HOME/bin/musl-32-bit/lib/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make[1]: *** [bin/release/premake4] Error 1
make: *** [Premake4] Error 2
相关行是:
/usr/bin/ld: skipping incompatible $HOME/bin/musl-32-bit/lib/libc.a when searching for -lc
现在我不明白的部分是,当我 ar x
在 [=17 的构建步骤中生成 libc.a
(进入文件夹 $HOME/bin/musl-32-bit/lib/libc
) =](见上文),它证明所有包含的对象似乎都是正确的目标体系结构(全部显示 ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
),正如我可以证明在发出以下命令时出现空的:
find $HOME/bin/musl-32-bit/lib -name '*.o' -exec file {} +|grep -v 'ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped'
事实上,这没有输出。同样,当使用相同的方法查看构建目录时,我找不到任何不符合我预期的目标文件。
为了更好的衡量,我决定还任务 objdump
告诉我更多关于 libc.a
的问题,并得出相同的结果:
objdump -a $HOME/bin/musl-32-bit/lib/libc.a|grep 'file format'|grep -v 'file format elf32-i386'
所以我的问题是双重的:
- 当 GCC 被要求 link 时,什么会取消静态库作为 "incompatible" 的资格?
- 我遇到的具体问题可能是什么?
第一个是我真正感兴趣的,但是对于第二个,我想与您分享解决此类问题的经验。例如,我错过了哪些验证步骤?
请注意 "native" premake4
可以很好地构建:
make -j 8 CC=$HOME/bin/musl-64-bit/bin/musl-gcc ARCH=-m64 LDFLAGS=-static verbose=1
从将 -v
标志添加到 LDFLAGS
时的输出来看,目标似乎始终停留在 x86_64-linux-gnu
。不过,我还没有想出解决这个问题的方法。
简而言之,musl-gcc
包装脚本设置不适合与 -m32
一起使用。我认为发生的事情是 musl-gcc
在默认(64 位)模式下调用实际编译器,然后生成的目标文件与(预期的 32 位)libc 不兼容。
如果您将 -m32
放入生成的包装器脚本中,它可能会起作用。如果您将 -m32
放在 $CC
(即 CC="gcc -m32"
)中而不是放在 $CFLAGS
.
中,这将在最新版本中自动发生
更新:正如已转移到聊天的讨论中所述,可能还需要添加 -Wl,-melf_i386
(由于 musl-gcc
包装器使用的规范文件中的缺陷不考虑-m32
支持)但似乎仍然不够。
解决方法
事实证明解决方案很简单。
我们需要告诉 linker 和 driver 使用 -m32
... 我以前就是那么远。然而,事实证明,缺少的部分是通过 CFLAGS
将 linker 选项传递给 driver,就像这样 -Wl,-melf_i386
.
我终于能够在 multilib-enabled 64 位主机上构建并 link 32 位可执行文件。
注意:下面的信息留给那些想了解我是如何调查这个问题的人。
好的,所以我进一步调查了这个问题,一旦您提取了 object 文件,输出就会变得更有启发性。要重现我正在做的事情,您可能必须使用 Bash 或允许 $(...)
的类似 shell,或者您需要相应地调整命令行。
首先,重要的是安装 gcc-multilib
和朋友,以便以 -m32
为目标(i386-linux-gnu
在这里恰好是 i686-linux-gnu
的别名)。
代码和 make 文件
我有以下生成文件:
CC?=$(HOME)/bin/musl/bin64/musl-gcc
BLDARCH?=-m64
CFLAGS+=-v $(BLDARCH)
LDFLAGS+=-v -static $(BLDARCH)
all: helloworld
helloworld: helloworld.c
clean:
rm -f helloworld
rebuild: clean all
.PHONY: clean rebuild
.NOTPARALLEL: rebuild
和下面的小helloworld.c
:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("Hello world!\n");
return 0;
}
和我的 32 位 musl-libc 分别安装到 $HOME/bin/musl/{bin,include,lib}32
。 64位的分别安装到$HOME/bin/musl/{bin,include,lib}64
。
正在尝试构建:
make CC=$HOME/bin/musl/bin32/musl-gcc BLDARCH=-m32 rebuild
总是以相同的无意义行失败:
/usr/bin/ld: skipping incompatible ~/bin/musl/lib32/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make: *** [helloworld] Error 1
挖掘细节
所以经过深思熟虑后,我决定 re-do gcc
driver 手动完成的步骤。
这意味着 运行 粗略(我用 ~
替换了所有出现的主文件夹):
- 编译:
/usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -quiet -imultilib 32 -imultiarch i386-linux-gnu helloworld.c -nostdinc -isystem ~/bin/musl/include32 -isystem /usr/lib/gcc/x86_64-linux-gnu/4.8/include -quiet -dumpbase helloworld.c -m32 -mtune=generic -march=i686 -auxbase helloworld -version -fstack-protector -Wformat -Wformat-security -o /tmp/ccGmMuR1.s
- Assemble:
as -v -v --32 -o /tmp/ccgRGlqf.o /tmp/ccGmMuR1.s
- Link:
env COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS="-v -v -static -m32 -o helloworld -specs=~/bin/musl/lib32/musl-gcc.specs -mtune=generic -march=i686" /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -z relro -o helloworld ~/bin/musl/lib32/crt1.o ~/bin/musl/lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtbegin.o -L~/bin/musl/lib32 -L /usr/lib/gcc/x86_64-linux-gnu/4.8/32/. /tmp/ccpL09mJ.o --start-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtend.o ~/bin/musl/lib32/crtn.o
显然这并没有稍微改变错误信息:
/usr/bin/ld: skipping incompatible ~/bin/musl/lib32/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
所以我从 collect2
的命令行中取出 -lc
并创建了一个文件夹 ~/bin/musl/lib32/archive
,我将 [=] 生成的整个 libc.a
提取到其中259=] 构建尝试。然后我指示 collect2
在哪里可以找到 object 文件,如下所示:
Link: env COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gc
c/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS="-v -v -
static -m32 -o helloworld -specs=~/bin/musl/lib32/musl-gcc.specs -mtune=generic -march=i686" /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -z relro -o helloworld $HOME/bin/musl/lib32/crt1.o ~/bin/musl/lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtbegin.o -L~/bin/musl/lib32 -L /usr/lib/gcc/x86_64-linux-gnu/4.8/32/. /tmp/ccpL09mJ.o --start-group /usr/lib/gcc/x86_64-linux-g
nu/4.8/32/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtend.o ~/bin/musl/lib32/crtn.o $(find ~/bin/musl/lib32/archive -type f -name '*.o')
这个要点是取出 -lc
并附加 $(find ~/bin/musl/lib32/archive -type f -name '*.o')
。
这给了我一大堆新的,但更有意义的错误,类似于以下错误:
/usr/bin/ld: Warning: size of symbol `__init_ssp' changed from 1 in ~/bin/musl/lib32/archive/__libc_start_main.o to 65 in ~/bin/musl/lib32/archive/__stack_chk_fail.o
/usr/bin/ld: Warning: size of symbol `__funcs_on_exit' changed from 126 in ~/bin/musl/lib32/archive/atexit.o to 1 in ~/bin/musl/lib32/archive/exit.o
# more of those
/usr/bin/ld: i386 architecture of input file `~/bin/musl/lib32/crt1.o' is incompatible with i386:x86-64 output
/usr/bin/ld: i386 architecture of input file `~/bin/musl/lib32/crti.o' is incompatible with i386:x86-64 output
# more of those
情节变厚了。显然 collect2
命令对要构建的内容有错误的想法。考虑到管理环境变量的输出,这很奇怪:
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-v' '-static' '-m32' '-o' 'helloworld' '-specs=~/bin/musl/lib32/musl-gcc.specs' '-mtune=generic' '-march=i686'
... 我使用 env
传递了它,试图重现 collect2
包装器在 linking libc.a
时遇到的情况。
为了了解更多关于 GNU 编译器内部的信息,需要阅读 https://gcc.gnu.org/onlinedocs/gccint/
不幸的是,关于 collect2
的部分对我们没有帮助。但是 /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 --help
的冗长输出看起来很有希望。
我们如何将命令行选项偷运到 collect2
?
现在,我打算尽我所能 运行 手动解决。所以我试图告诉 linker 我期望的输出格式。根据支持的目标列表,我对 elf_i386
.
感兴趣
在上一行末尾传递 -melf_386
给出了一个有趣的错误输出。 __vsyscall
、__moddi3
和 __divdi3
等许多引用函数未定义。这表明它们根本不存在于静态库的 object 文件中或任何启动 .o
文件中:
~/bin/musl/lib32/archive/aio.o: In function `cleanup':
aio.c:(.text+0x5ad): undefined reference to `__vsyscall'
aio.c:(.text+0x5bb): undefined reference to `__vsyscall'
aio.c:(.text+0x5e8): undefined reference to `__vsyscall'
aio.c:(.text+0x5f6): undefined reference to `__vsyscall'
aio.c:(.text+0x61d): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/aio.o:aio.c:(.text+0x62b): more undefined references to `__vsyscall' follow
~/bin/musl/lib32/archive/cpow.o: In function `cpow':
cpow.c:(.text+0x4f): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/cpowf.o: In function `cpowf':
cpowf.c:(.text+0x47): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/cpowl.o: In function `cpowl':
cpowl.c:(.text+0x4c): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/sysconf.o: In function `sysconf':
sysconf.c:(.text+0xcc): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/__getdents.o: In function `__getdents':
__getdents.c:(.text+0x13): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/opendir.o: In function `opendir':
opendir.c:(.text+0x37): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/readdir.o: In function `readdir':
readdir.c:(.text+0x1f): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/__init_tls.o: In function `__init_tls':
__init_tls.c:(.text+0x136): undefined reference to `__vsyscall6'
__init_tls.c:(.text+0x16e): undefined reference to `__vsyscall'
正如我在 问题 中所阐明的,存档中的 object 文件都表明它们是 elf32-i386
。
函数 __vsyscall
和 __vsyscall6
应该在名为 syscall.o
的文件中结束,给定 musl-libc 中 i386
的源文件:src/internal/i386/syscall.s
.让我们先验证一下。由于还有一个文件 src/misc/syscall.c
,但名称可能不同。四个文件的文件名中有syscall
:
__syscall_cp.o
syscall_cp.o
syscall.o
syscall_ret.o
使用 nm
查询这些文件得到:
$ nm -s $(ls |grep syscall)
__syscall_cp.o:
00000000 t sccp
U __syscall
00000005 T __syscall_cp
00000000 W __syscall_cp_c
syscall_cp.o:
U __cancel
00000008 T __cp_begin
00000035 T __cp_cancel
00000030 T __cp_end
00000000 T __syscall_cp_asm
syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
syscall_ret.o:
U __errno_location
00000000 T __syscall_ret
符号类型为 U
的符号未定义,因此 linker 期望它们来自外部(每个 object 文件外部)。
需要最后的 $ nm --defined-only *.o ../*.o|grep vsyscall
来验证 libc.a
中确实缺少这些符号。
所以cross-builtlibc.a
到底是哪里出了问题。回到绘图板。
我希望这个描述能帮助其他人找出类似的问题并了解 GCC 的幕后情况。
传奇还在继续
看到真的很惊讶:
$ nm --defined-only ../libc.a |grep -B 2 vsyscall
syscall.o:
0000004b T __syscall
00000000 T __vsyscall
00000031 T __vsyscall6
但对于提取的 object 文件,相应的命令 (nm --defined-only *.o ../*.o|grep -B 2 vsyscall
) 将不会产生任何输出。
所以在 libc.a
中不知何故 nm
看到了两个符号,但在提取它们之后它们消失了?奇数
让我们在libc.a
中寻找syscall.o
:
$ nm ../libc.a |grep ^syscall
syscall.o:
syscall_ret.o:
syscall.o:
syscall_cp.o:
哇哦?所以 syscall.o
在 stati 中存在两次图书馆?好吧,这看起来可能正是我们正在寻找的错误原因。它确实解释了符号消失的原因。很可能后者 syscall.o
覆盖了 运行ning ar x ...
.
时第一次提取的那个
正在确认:
$ nm ../libc.a |grep -A 4 ^syscall\.o
syscall.o:
0000004b T __syscall
U __sysinfo
00000000 T __vsyscall
00000031 T __vsyscall6
--
syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
并在完成 32 位构建后查看 musl-libc 源代码树:
$ find . -type f -name 'syscall.o' -exec nm {} +
./src/internal/syscall.o:
0000004b T __syscall
U __sysinfo
00000000 T __vsyscall
00000031 T __vsyscall6
./src/misc/syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
以不与现有名称冲突的名称将前者复制到 lib32/archive
目录中会在其他函数上产生更多错误,这表明其他 object 文件也可能作为副本存在于生成的文件中libc.a
.
哪些是重复的?
$ diff <(nm libc.a|grep ':$'|cut -f 1 -d :|sort) <(nm libc.a|grep ':$'|cut -f 1 -d :|sort -u)
--- /dev/fd/63 2015-10-05 23:58:53.683804823 +0000
+++ /dev/fd/62 2015-10-05 23:58:53.683804823 +0000
@@ -131,7 +131,6 @@
clogl.o
clog.o
clone.o
-clone.o
closedir.o
close.o
cnd_broadcast.o
@@ -1115,7 +1114,6 @@
__syscall_cp.o
syscall_cp.o
syscall.o
-syscall.o
syscall_ret.o
sysconf.o
sysinfo.o
这样我们可以看到 clone.o
和 syscall.o
受到重复影响。这表明某些 object 文件从 libc.a
中完全丢失,给出了对以下符号的未定义引用:
__divdi3
__moddi3
__mulxc3
__tls_get_new
__udivdi3
__umoddi3
这些名称恰好与 GCC 列出的 Integer library routines 中的名称一致。这让我觉得我缺少一个由 GCC 提供给 link 的库。喜欢libgcc
?! ...
我有包 lib32gcc-4.8-dev
这意味着我应该有所需的文件:
$ apt-file list lib32gcc-4.8-dev|grep -E 'libgcc.*\.a'
lib32gcc-4.8-dev: /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc.a
lib32gcc-4.8-dev: /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a
在上一步之后,我决定在 Ubuntu 14.04 上设置一个 x86_32 版本,该版本基本上处于同一补丁级别。
然后我将 x86_32 机器的 libgcc.a
与 x86_64 机器的 libgcc.a
进行了比较。除了少数函数的 "symbol value"(用 nm
行话)外,它们几乎完全相同。
此外,由于符号在静态库中,我再次尝试 link 对静态库进行攻击。这仅适用于 linker (collect2
) 命令行上的 -melf_i386
。
在尝试使用 LDFLAGS
并注意到这些也被传递给 cc1
后,我将附加 -Wl,-melf_i386
设置为 CFLAGS
,现在它起作用了。太棒了。
作为 side-note:我还在命令行中将 /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2
换成了 ld
,其中 collect2
应该是一个包装器。错误输出是相同的。
所以我想做的是 Ubuntu 14.04 (x86_64) 我想根据最新发布的 1.1.11 版本设置 musl-libc
这一刻。
我所做的是:
- 为 GCC 安装 multilib 支持:
sudo apt-get --no-install-recommends install gcc-multilib
- 分别为 32 位和 64 位配置库并将它们安装到单独的文件夹中:
CFLAGS=-m32 ./configure --prefix=$HOME/bin/musl-32-bit --disable-shared --target=i386-linux-gnu && make && make install
CFLAGS=-m64 ./configure --prefix=$HOME/bin/musl-64-bit --disable-shared --target=x86_64-linux-gnu
然后为了构建静态 linked premake4
,我在 premake4
:
Makefile
上像这样调用 GNU make
make -j 8 CC=$HOME/bin/musl-32-bit/bin/musl-gcc ARCH=-m32 LDFLAGS="-v -static" verbose=1
这似乎适用于 linking 步骤,其中包含:
Linking Premake4
$HOME/bin/musl-32-bit/bin/musl-gcc -o bin/release/premake4 intermediate/gmake__/premake.o intermediate/gmake__/os_uuid.o intermediate/gmake__/os_pathsearch.o intermediate/gmake__/os_match.o intermediate/gmake__/os_chdir.o intermediate/gmake__/os_mkdir.o intermediate/gmake__/os_stat.o intermediate/gmake__/os_getversion.o intermediate/gmake__/premake_main.o intermediate/gmake__/os_isdir.o intermediate/gmake__/string_endswith.o intermediate/gmake__/os_isfile.o intermediate/gmake__/scripts.o intermediate/gmake__/path_isabsolute.o intermediate/gmake__/os_rmdir.o intermediate/gmake__/os_getcwd.o intermediate/gmake__/os_is64bit.o intermediate/gmake__/os_copyfile.o intermediate/gmake__/lstate.o intermediate/gmake__/ltable.o intermediate/gmake__/lgc.o intermediate/gmake__/lobject.o intermediate/gmake__/lcode.o intermediate/gmake__/lmathlib.o intermediate/gmake__/lbaselib.o intermediate/gmake__/lmem.o intermediate/gmake__/lfunc.o intermediate/gmake__/lparser.o intermediate/gmake__/ldblib.o intermediate/gmake__/lzio.o intermediate/gmake__/lstrlib.o intermediate/gmake__/lvm.o intermediate/gmake__/lauxlib.o intermediate/gmake__/llex.o intermediate/gmake__/lstring.o intermediate/gmake__/ldump.o intermediate/gmake__/ldebug.o intermediate/gmake__/loadlib.o intermediate/gmake__/lopcodes.o intermediate/gmake__/linit.o intermediate/gmake__/ldo.o intermediate/gmake__/lapi.o intermediate/gmake__/liolib.o intermediate/gmake__/loslib.o intermediate/gmake__/lundump.o intermediate/gmake__/ltm.o intermediate/gmake__/ltablib.o -v -static -L. -s -rdynamic -lm -ldl
Using built-in specs.
Reading specs from $HOME/bin/musl-32-bit/lib/musl-gcc.specs
rename spec cpp_options to old_cpp_options
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.8/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.8.4-2ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.8/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.8 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.8 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-libmudflap --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.8-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.8-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-m32' '-o' 'bin/release/premake4' '-v' '-static' '-L.' '-s' '-rdynamic' '-specs=$HOME/bin/musl-32-bit/lib/musl-gcc.specs' '-mtune=generic' '-march=i686'
/usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -export-dynamic -z relro -o bin/release/premake4 -s $HOME/bin/musl-32-bit/lib/crt1.o $HOME/bin/musl-32-bit/lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/crtbegin.o -L. -L$HOME/bin/musl-32-bit/lib -L /usr/lib/gcc/x86_64-linux-gnu/4.8/. intermediate/gmake__/premake.o intermediate/gmake__/os_uuid.o intermediate/gmake__/os_pathsearch.o intermediate/gmake__/os_match.o intermediate/gmake__/os_chdir.o intermediate/gmake__/os_mkdir.o intermediate/gmake__/os_stat.o intermediate/gmake__/os_getversion.o intermediate/gmake__/premake_main.o intermediate/gmake__/os_isdir.o intermediate/gmake__/string_endswith.o intermediate/gmake__/os_isfile.o intermediate/gmake__/scripts.o intermediate/gmake__/path_isabsolute.o intermediate/gmake__/os_rmdir.o intermediate/gmake__/os_getcwd.o intermediate/gmake__/os_is64bit.o intermediate/gmake__/os_copyfile.o intermediate/gmake__/lstate.o intermediate/gmake__/ltable.o intermediate/gmake__/lgc.o intermediate/gmake__/lobject.o intermediate/gmake__/lcode.o intermediate/gmake__/lmathlib.o intermediate/gmake__/lbaselib.o intermediate/gmake__/lmem.o intermediate/gmake__/lfunc.o intermediate/gmake__/lparser.o intermediate/gmake__/ldblib.o intermediate/gmake__/lzio.o intermediate/gmake__/lstrlib.o intermediate/gmake__/lvm.o intermediate/gmake__/lauxlib.o intermediate/gmake__/llex.o intermediate/gmake__/lstring.o intermediate/gmake__/ldump.o intermediate/gmake__/ldebug.o intermediate/gmake__/loadlib.o intermediate/gmake__/lopcodes.o intermediate/gmake__/linit.o intermediate/gmake__/ldo.o intermediate/gmake__/lapi.o intermediate/gmake__/liolib.o intermediate/gmake__/loslib.o intermediate/gmake__/lundump.o intermediate/gmake__/ltm.o intermediate/gmake__/ltablib.o -lm -ldl --start-group /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/libgcc_eh.a -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/crtend.o $HOME/bin/musl-32-bit/lib/crtn.o
/usr/bin/ld: skipping incompatible $HOME/bin/musl-32-bit/lib/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make[1]: *** [bin/release/premake4] Error 1
make: *** [Premake4] Error 2
相关行是:
/usr/bin/ld: skipping incompatible $HOME/bin/musl-32-bit/lib/libc.a when searching for -lc
现在我不明白的部分是,当我 ar x
在 [=17 的构建步骤中生成 libc.a
(进入文件夹 $HOME/bin/musl-32-bit/lib/libc
) =](见上文),它证明所有包含的对象似乎都是正确的目标体系结构(全部显示 ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
),正如我可以证明在发出以下命令时出现空的:
find $HOME/bin/musl-32-bit/lib -name '*.o' -exec file {} +|grep -v 'ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped'
事实上,这没有输出。同样,当使用相同的方法查看构建目录时,我找不到任何不符合我预期的目标文件。
为了更好的衡量,我决定还任务 objdump
告诉我更多关于 libc.a
的问题,并得出相同的结果:
objdump -a $HOME/bin/musl-32-bit/lib/libc.a|grep 'file format'|grep -v 'file format elf32-i386'
所以我的问题是双重的:
- 当 GCC 被要求 link 时,什么会取消静态库作为 "incompatible" 的资格?
- 我遇到的具体问题可能是什么?
第一个是我真正感兴趣的,但是对于第二个,我想与您分享解决此类问题的经验。例如,我错过了哪些验证步骤?
请注意 "native" premake4
可以很好地构建:
make -j 8 CC=$HOME/bin/musl-64-bit/bin/musl-gcc ARCH=-m64 LDFLAGS=-static verbose=1
从将 -v
标志添加到 LDFLAGS
时的输出来看,目标似乎始终停留在 x86_64-linux-gnu
。不过,我还没有想出解决这个问题的方法。
简而言之,musl-gcc
包装脚本设置不适合与 -m32
一起使用。我认为发生的事情是 musl-gcc
在默认(64 位)模式下调用实际编译器,然后生成的目标文件与(预期的 32 位)libc 不兼容。
如果您将 -m32
放入生成的包装器脚本中,它可能会起作用。如果您将 -m32
放在 $CC
(即 CC="gcc -m32"
)中而不是放在 $CFLAGS
.
更新:正如已转移到聊天的讨论中所述,可能还需要添加 -Wl,-melf_i386
(由于 musl-gcc
包装器使用的规范文件中的缺陷不考虑-m32
支持)但似乎仍然不够。
解决方法
事实证明解决方案很简单。
我们需要告诉 linker 和 driver 使用 -m32
... 我以前就是那么远。然而,事实证明,缺少的部分是通过 CFLAGS
将 linker 选项传递给 driver,就像这样 -Wl,-melf_i386
.
我终于能够在 multilib-enabled 64 位主机上构建并 link 32 位可执行文件。
注意:下面的信息留给那些想了解我是如何调查这个问题的人。
好的,所以我进一步调查了这个问题,一旦您提取了 object 文件,输出就会变得更有启发性。要重现我正在做的事情,您可能必须使用 Bash 或允许 $(...)
的类似 shell,或者您需要相应地调整命令行。
首先,重要的是安装 gcc-multilib
和朋友,以便以 -m32
为目标(i386-linux-gnu
在这里恰好是 i686-linux-gnu
的别名)。
代码和 make 文件
我有以下生成文件:
CC?=$(HOME)/bin/musl/bin64/musl-gcc
BLDARCH?=-m64
CFLAGS+=-v $(BLDARCH)
LDFLAGS+=-v -static $(BLDARCH)
all: helloworld
helloworld: helloworld.c
clean:
rm -f helloworld
rebuild: clean all
.PHONY: clean rebuild
.NOTPARALLEL: rebuild
和下面的小helloworld.c
:
#include <stdio.h>
int main(int argc, char** argv)
{
printf("Hello world!\n");
return 0;
}
和我的 32 位 musl-libc 分别安装到 $HOME/bin/musl/{bin,include,lib}32
。 64位的分别安装到$HOME/bin/musl/{bin,include,lib}64
。
正在尝试构建:
make CC=$HOME/bin/musl/bin32/musl-gcc BLDARCH=-m32 rebuild
总是以相同的无意义行失败:
/usr/bin/ld: skipping incompatible ~/bin/musl/lib32/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
make: *** [helloworld] Error 1
挖掘细节
所以经过深思熟虑后,我决定 re-do gcc
driver 手动完成的步骤。
这意味着 运行 粗略(我用 ~
替换了所有出现的主文件夹):
- 编译:
/usr/lib/gcc/x86_64-linux-gnu/4.8/cc1 -quiet -imultilib 32 -imultiarch i386-linux-gnu helloworld.c -nostdinc -isystem ~/bin/musl/include32 -isystem /usr/lib/gcc/x86_64-linux-gnu/4.8/include -quiet -dumpbase helloworld.c -m32 -mtune=generic -march=i686 -auxbase helloworld -version -fstack-protector -Wformat -Wformat-security -o /tmp/ccGmMuR1.s
- Assemble:
as -v -v --32 -o /tmp/ccgRGlqf.o /tmp/ccGmMuR1.s
- Link:
env COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS="-v -v -static -m32 -o helloworld -specs=~/bin/musl/lib32/musl-gcc.specs -mtune=generic -march=i686" /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -z relro -o helloworld ~/bin/musl/lib32/crt1.o ~/bin/musl/lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtbegin.o -L~/bin/musl/lib32 -L /usr/lib/gcc/x86_64-linux-gnu/4.8/32/. /tmp/ccpL09mJ.o --start-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a -lc --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtend.o ~/bin/musl/lib32/crtn.o
显然这并没有稍微改变错误信息:
/usr/bin/ld: skipping incompatible ~/bin/musl/lib32/libc.a when searching for -lc
/usr/bin/ld: cannot find -lc
collect2: error: ld returned 1 exit status
所以我从 collect2
的命令行中取出 -lc
并创建了一个文件夹 ~/bin/musl/lib32/archive
,我将 [=] 生成的整个 libc.a
提取到其中259=] 构建尝试。然后我指示 collect2
在哪里可以找到 object 文件,如下所示:
Link: env COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gc
c/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS="-v -v -
static -m32 -o helloworld -specs=~/bin/musl/lib32/musl-gcc.specs -mtune=generic -march=i686" /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 -dynamic-linker /lib/ld-musl-i386.so.1 -nostdlib -static -z relro -o helloworld $HOME/bin/musl/lib32/crt1.o ~/bin/musl/lib32/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtbegin.o -L~/bin/musl/lib32 -L /usr/lib/gcc/x86_64-linux-gnu/4.8/32/. /tmp/ccpL09mJ.o --start-group /usr/lib/gcc/x86_64-linux-g
nu/4.8/32/libgcc.a /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a --end-group /usr/lib/gcc/x86_64-linux-gnu/4.8/32/crtend.o ~/bin/musl/lib32/crtn.o $(find ~/bin/musl/lib32/archive -type f -name '*.o')
这个要点是取出 -lc
并附加 $(find ~/bin/musl/lib32/archive -type f -name '*.o')
。
这给了我一大堆新的,但更有意义的错误,类似于以下错误:
/usr/bin/ld: Warning: size of symbol `__init_ssp' changed from 1 in ~/bin/musl/lib32/archive/__libc_start_main.o to 65 in ~/bin/musl/lib32/archive/__stack_chk_fail.o
/usr/bin/ld: Warning: size of symbol `__funcs_on_exit' changed from 126 in ~/bin/musl/lib32/archive/atexit.o to 1 in ~/bin/musl/lib32/archive/exit.o
# more of those
/usr/bin/ld: i386 architecture of input file `~/bin/musl/lib32/crt1.o' is incompatible with i386:x86-64 output
/usr/bin/ld: i386 architecture of input file `~/bin/musl/lib32/crti.o' is incompatible with i386:x86-64 output
# more of those
情节变厚了。显然 collect2
命令对要构建的内容有错误的想法。考虑到管理环境变量的输出,这很奇怪:
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.8/32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.8/:/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-v' '-static' '-m32' '-o' 'helloworld' '-specs=~/bin/musl/lib32/musl-gcc.specs' '-mtune=generic' '-march=i686'
... 我使用 env
传递了它,试图重现 collect2
包装器在 linking libc.a
时遇到的情况。
为了了解更多关于 GNU 编译器内部的信息,需要阅读 https://gcc.gnu.org/onlinedocs/gccint/
不幸的是,关于 collect2
的部分对我们没有帮助。但是 /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2 --help
的冗长输出看起来很有希望。
我们如何将命令行选项偷运到 collect2
?
现在,我打算尽我所能 运行 手动解决。所以我试图告诉 linker 我期望的输出格式。根据支持的目标列表,我对 elf_i386
.
在上一行末尾传递 -melf_386
给出了一个有趣的错误输出。 __vsyscall
、__moddi3
和 __divdi3
等许多引用函数未定义。这表明它们根本不存在于静态库的 object 文件中或任何启动 .o
文件中:
~/bin/musl/lib32/archive/aio.o: In function `cleanup':
aio.c:(.text+0x5ad): undefined reference to `__vsyscall'
aio.c:(.text+0x5bb): undefined reference to `__vsyscall'
aio.c:(.text+0x5e8): undefined reference to `__vsyscall'
aio.c:(.text+0x5f6): undefined reference to `__vsyscall'
aio.c:(.text+0x61d): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/aio.o:aio.c:(.text+0x62b): more undefined references to `__vsyscall' follow
~/bin/musl/lib32/archive/cpow.o: In function `cpow':
cpow.c:(.text+0x4f): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/cpowf.o: In function `cpowf':
cpowf.c:(.text+0x47): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/cpowl.o: In function `cpowl':
cpowl.c:(.text+0x4c): undefined reference to `__mulxc3'
~/bin/musl/lib32/archive/sysconf.o: In function `sysconf':
sysconf.c:(.text+0xcc): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/__getdents.o: In function `__getdents':
__getdents.c:(.text+0x13): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/opendir.o: In function `opendir':
opendir.c:(.text+0x37): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/readdir.o: In function `readdir':
readdir.c:(.text+0x1f): undefined reference to `__vsyscall'
~/bin/musl/lib32/archive/__init_tls.o: In function `__init_tls':
__init_tls.c:(.text+0x136): undefined reference to `__vsyscall6'
__init_tls.c:(.text+0x16e): undefined reference to `__vsyscall'
正如我在 问题 中所阐明的,存档中的 object 文件都表明它们是 elf32-i386
。
函数 __vsyscall
和 __vsyscall6
应该在名为 syscall.o
的文件中结束,给定 musl-libc 中 i386
的源文件:src/internal/i386/syscall.s
.让我们先验证一下。由于还有一个文件 src/misc/syscall.c
,但名称可能不同。四个文件的文件名中有syscall
:
__syscall_cp.o
syscall_cp.o
syscall.o
syscall_ret.o
使用 nm
查询这些文件得到:
$ nm -s $(ls |grep syscall)
__syscall_cp.o:
00000000 t sccp
U __syscall
00000005 T __syscall_cp
00000000 W __syscall_cp_c
syscall_cp.o:
U __cancel
00000008 T __cp_begin
00000035 T __cp_cancel
00000030 T __cp_end
00000000 T __syscall_cp_asm
syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
syscall_ret.o:
U __errno_location
00000000 T __syscall_ret
符号类型为 U
的符号未定义,因此 linker 期望它们来自外部(每个 object 文件外部)。
需要最后的 $ nm --defined-only *.o ../*.o|grep vsyscall
来验证 libc.a
中确实缺少这些符号。
所以cross-builtlibc.a
到底是哪里出了问题。回到绘图板。
我希望这个描述能帮助其他人找出类似的问题并了解 GCC 的幕后情况。
传奇还在继续
看到真的很惊讶:
$ nm --defined-only ../libc.a |grep -B 2 vsyscall
syscall.o:
0000004b T __syscall
00000000 T __vsyscall
00000031 T __vsyscall6
但对于提取的 object 文件,相应的命令 (nm --defined-only *.o ../*.o|grep -B 2 vsyscall
) 将不会产生任何输出。
所以在 libc.a
中不知何故 nm
看到了两个符号,但在提取它们之后它们消失了?奇数
让我们在libc.a
中寻找syscall.o
:
$ nm ../libc.a |grep ^syscall
syscall.o:
syscall_ret.o:
syscall.o:
syscall_cp.o:
哇哦?所以 syscall.o
在 stati 中存在两次图书馆?好吧,这看起来可能正是我们正在寻找的错误原因。它确实解释了符号消失的原因。很可能后者 syscall.o
覆盖了 运行ning ar x ...
.
正在确认:
$ nm ../libc.a |grep -A 4 ^syscall\.o
syscall.o:
0000004b T __syscall
U __sysinfo
00000000 T __vsyscall
00000031 T __vsyscall6
--
syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
并在完成 32 位构建后查看 musl-libc 源代码树:
$ find . -type f -name 'syscall.o' -exec nm {} +
./src/internal/syscall.o:
0000004b T __syscall
U __sysinfo
00000000 T __vsyscall
00000031 T __vsyscall6
./src/misc/syscall.o:
00000000 T syscall
U __syscall_ret
U __vsyscall6
以不与现有名称冲突的名称将前者复制到 lib32/archive
目录中会在其他函数上产生更多错误,这表明其他 object 文件也可能作为副本存在于生成的文件中libc.a
.
哪些是重复的?
$ diff <(nm libc.a|grep ':$'|cut -f 1 -d :|sort) <(nm libc.a|grep ':$'|cut -f 1 -d :|sort -u)
--- /dev/fd/63 2015-10-05 23:58:53.683804823 +0000
+++ /dev/fd/62 2015-10-05 23:58:53.683804823 +0000
@@ -131,7 +131,6 @@
clogl.o
clog.o
clone.o
-clone.o
closedir.o
close.o
cnd_broadcast.o
@@ -1115,7 +1114,6 @@
__syscall_cp.o
syscall_cp.o
syscall.o
-syscall.o
syscall_ret.o
sysconf.o
sysinfo.o
这样我们可以看到 clone.o
和 syscall.o
受到重复影响。这表明某些 object 文件从 libc.a
中完全丢失,给出了对以下符号的未定义引用:
__divdi3
__moddi3
__mulxc3
__tls_get_new
__udivdi3
__umoddi3
这些名称恰好与 GCC 列出的 Integer library routines 中的名称一致。这让我觉得我缺少一个由 GCC 提供给 link 的库。喜欢libgcc
?! ...
我有包 lib32gcc-4.8-dev
这意味着我应该有所需的文件:
$ apt-file list lib32gcc-4.8-dev|grep -E 'libgcc.*\.a'
lib32gcc-4.8-dev: /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc.a
lib32gcc-4.8-dev: /usr/lib/gcc/x86_64-linux-gnu/4.8/32/libgcc_eh.a
在上一步之后,我决定在 Ubuntu 14.04 上设置一个 x86_32 版本,该版本基本上处于同一补丁级别。
然后我将 x86_32 机器的 libgcc.a
与 x86_64 机器的 libgcc.a
进行了比较。除了少数函数的 "symbol value"(用 nm
行话)外,它们几乎完全相同。
此外,由于符号在静态库中,我再次尝试 link 对静态库进行攻击。这仅适用于 linker (collect2
) 命令行上的 -melf_i386
。
在尝试使用 LDFLAGS
并注意到这些也被传递给 cc1
后,我将附加 -Wl,-melf_i386
设置为 CFLAGS
,现在它起作用了。太棒了。
作为 side-note:我还在命令行中将 /usr/lib/gcc/x86_64-linux-gnu/4.8/collect2
换成了 ld
,其中 collect2
应该是一个包装器。错误输出是相同的。