And*_*ose 5 linux embedded gcc libc cross-compiling
我(反复)遇到了各种公司的嵌入式 Linux 产品的问题,其中的 GPL 源代码与系统上实际运行的代码不匹配。它“接近”,但不太正确,特别是相对于他们使用的标准 C 库而言。这不是违反GPL吗?
通常,这种不匹配会导致程序员(像我一样)进行交叉编译,但在程序运行时设备却神秘地回复“找不到文件”或类似的信息。
我并不是唯一一个遇到此类问题的人 - 对于许多人来说,都有与该问题直接或间接相关的线程:例如: 基于 MIPS 的代码源工具链的编译参数?
我在 Sony 设备、D-link 和许多其他设备上都遇到过这个问题。这很常见。
制作新库并不是一个好的解决方案,因为大多数系统只是 ROMFS,并且 LD_LIBRARY_PATH 有时会损坏 - 因此在设备上安装新库会浪费非常有限的内存,并且通常无法工作。
如果我知道该库的正确源代码版本是什么,我就可以绕过制造商的粗心大意,并从原始开发人员树中编译它;但是,当我拥有的只是库本身的二进制文件时,如何找到我需要的版本?
例如:我在 DSL 调制解调器的 libc 上运行 elfread -a libc.so.0 (见下文);但我在这里没有看到任何可以告诉我它到底是哪个 libc 的内容......
如何从库的二进制文件中找到源代码的名称或标识符,以便我可以使用该库创建交叉编译器?例如:谁能告诉我这个库的源代码来自什么,以及他们是如何知道的?
ELF Header:
Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, big endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: MIPS R3000
Version: 0x1
Entry point address: 0x5a60
Start of program headers: 52 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x1007, noreorder, pic, cpic, o32, mips1
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 4
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0
There are no sections in this file.
There are no sections to group in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
REGINFO 0x0000b4 0x000000b4 0x000000b4 0x00018 0x00018 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x2c9ee 0x2c9ee R E 0x1000
LOAD 0x02c9f0 0x0006c9f0 0x0006c9f0 0x009a0 0x040b8 RW 0x1000
DYNAMIC 0x0000cc 0x000000cc 0x000000cc 0x0579a 0x0579a RWE 0x4
Dynamic section at offset 0xcc contains 19 entries:
Tag Type Name/Value
0x0000000e (SONAME) Library soname: [libc.so.0]
0x00000004 (HASH) 0x18c
0x00000005 (STRTAB) 0x3e9c
0x00000006 (SYMTAB) 0x144c
0x0000000a (STRSZ) 6602 (bytes)
0x0000000b (SYMENT) 16 (bytes)
0x00000015 (DEBUG) 0x0
0x00000003 (PLTGOT) 0x6ce20
0x00000011 (REL) 0x5868
0x00000012 (RELSZ) 504 (bytes)
0x00000013 (RELENT) 8 (bytes)
0x70000001 (MIPS_RLD_VERSION) 1
0x70000005 (MIPS_FLAGS) NOTPOT
0x70000006 (MIPS_BASE_ADDRESS) 0x0
0x7000000a (MIPS_LOCAL_GOTNO) 11
0x70000011 (MIPS_SYMTABNO) 677
0x70000012 (MIPS_UNREFEXTNO) 17
0x70000013 (MIPS_GOTSYM) 0x154
0x00000000 (NULL) 0x0
There are no relocations in this file.
The decoding of unwind sections for machine type MIPS R3000 is not currently supported.
Histogram for bucket list length (total of 521 buckets):
Length Number % of total Coverage
0 144 ( 27.6%)
1 181 ( 34.7%) 27.1%
2 130 ( 25.0%) 66.0%
3 47 ( 9.0%) 87.1%
4 12 ( 2.3%) 94.3%
5 5 ( 1.0%) 98.1%
6 1 ( 0.2%) 99.0%
7 1 ( 0.2%) 100.0%
No version information found in this file.
Primary GOT:
Canonical gp value: 00074e10
Reserved entries:
Address Access Initial Purpose
0006ce20 -32752(gp) 00000000 Lazy resolver
0006ce24 -32748(gp) 80000000 Module pointer (GNU extension)
Local entries:
Address Access Initial
0006ce28 -32744(gp) 00070000
0006ce2c -32740(gp) 00030000
0006ce30 -32736(gp) 00000000
0006ce34 -32732(gp) 00010000
0006ce38 -32728(gp) 0006d810
0006ce3c -32724(gp) 0006d814
0006ce40 -32720(gp) 00020000
0006ce44 -32716(gp) 00000000
0006ce48 -32712(gp) 00000000
Global entries:
Address Access Initial Sym.Val. Type Ndx Name
0006ce4c -32708(gp) 000186c0 000186c0 FUNC bad section index[ 6] __fputc_unlocked
0006ce50 -32704(gp) 000211a4 000211a4 FUNC bad section index[ 6] sigprocmask
0006ce54 -32700(gp) 0001e2b4 0001e2b4 FUNC bad section index[ 6] free
0006ce58 -32696(gp) 00026940 00026940 FUNC bad section index[ 6] raise
...
truncated listing
....
Run Code Online (Sandbox Code Playgroud)
注意:这篇文章的其余部分是一篇博客,展示了我如何提出上述问题并将有关该主题的有用信息放在一个地方。不要费心阅读它,除非你想知道我实际上确实研究过这个问题......详细地......以及如何不回答我的问题。
让 libc 程序在(例如)D-link 调制解调器上运行的正确(理论上)方法就是从制造商处获取产品的真实源代码,然后针对这些库进行编译......(它是 GPL !?对吧,所以法律是站在我们这边的,对吧?)
例如:我刚刚购买了一个 D-Link DSL-520B 调制解调器和一个 526B 调制解调器,但事后发现制造商“忘记”提供 520B 的 Linux 源代码,但确实提供了 526B 的 Linux 源代码。我在线检查了所有 DSL-5xxB 设备的源代码和工具链,令我高兴的是,所有这些设备(包括 526B)都包含相同的预编译 libc.so.0,MD5sum 为 6ed709113ce615e9f170aafa0eac04a6 。因此从理论上讲,DSL-5xxB 系列中所有受支持的调制解调器似乎都使用相同的 libc 库……我希望我能够使用该库。
但在我弄清楚如何让 DSL 调制解调器本身向我发送已安装的 /lib/libc.so.0 库的副本之后,我厌恶地发现它们都使用 MD5 总和为 b8d492decc8207e724a0822641205078 的库。在我购买的调制解调器(无论是否支持)中都没有找到与源代码工具链中包含的库相同的库。
为了验证 D-link 的工具链是否有缺陷,我没有编译程序(工具链无论如何都无法在我的 PC 上运行,因为它是错误的二进制格式)——但我发现工具链有一些预编译的 mips已经有二进制文件了;所以我只是下载了一个到调制解调器并 chmod +x ——然后(惊讶地)我收到了消息“找不到文件”。当我尝试运行它时......它不会运行。
所以,我立刻就知道工具链不好,但不知道具体原因。
我决定获取更新版本的 MIPS GCC(二进制版本),它应该具有更少的错误、更多的功能,并且大多数 PC 平台都支持它。这是要走的路!
从上述站点选择旧的“mips”版本以获得正确的 FTP 页面后,我升级到 gcc 4.9.0;并将我的 shell 的 PATH 变量设置为安装后交叉编译器的 /bin 目录。
然后我将D-link源代码包中的所有头文件和库复制到新的交叉编译器中,只是为了验证它是否可以编译D-link libc二进制文件。第一次尝试就成功了,编译了“hello world!” mips 32 大尾数法二进制文件中没有警告或错误。
(开始编辑:)@ChrisStratton 在评论中指出(在这篇文章之后),我对工具链的测试是不充分的,并且使用较新的 GCC 和较旧的库(即使它链接正确)作为测试是有缺陷的。我希望有一种方法可以为他的评论打分——我已经确信他是对的;尽管这使得 D-link 的做法变得更加糟糕——因为无法从调制解调器上的二进制文件知道他们实际使用的是哪个 GCC。用于内核的 GCC 不一定与用户空间中使用的 GCC 相同。
为了测试新编译器与调制解调器的兼容性并制作工具,以便我可以获得调制解调器上找到的实际库的副本:(结束编辑)我编写了一个根本不使用 C 库的程序(但是分为两部分):它运行得很好......并且附加了代码以显示如何完成它。
第一个清单是一个汇编语言程序,用于绕过 MIPS 上的标准 C 库的链接;第二个清单是一个旨在仅使用 Linux 内核创建二进制文件/流的八进制数字转储的程序。例如:它可以通过 telnet、netcat 等通过 ash/bash 或 busybox 复制/粘贴或编写二进制数据脚本:) 就像穷人的 uucp 一样。
// substart.S MIPS assembly language bypass of libc startup code
// it just calls main, and then jumps to the exit function
.text
.globl __start
__start: .ent __start
.frame $29, 32, $31
.set noreorder
.cpload $25
.set reorder
.cprestore 16
jal main
j exit
.end __start
// end substart.S
Run Code Online (Sandbox Code Playgroud)
...和...
// octdump.c
// To compile w/o libc :
// mips-linux-gcc stubstart.S octdump.c -nostdlib -o octdump
// To compile with working libc (eg: x86 system) :
// gcc octdump.c -o octdump_x86
#include <syscall.h>
#include <errno.h>
#include <sys/types.h>
int* __errno_location(void) { return &errno; }
#ifdef _syscall1
// define three unix functions (exit,read,write) in terms of unix syscall macros.
_syscall1( void, exit, int, status );
_syscall3( ssize_t, read, int, fd, void*, buf, size_t, count );
_syscall3( ssize_t, write, int, fd, const void*, buf, size_t, count );
#endif
#include <unistd.h>
void oct( unsigned char c ) {
unsigned int n = c;
int m=6;
static unsigned char oval[6]={'\\','\\','0','0','0','0'};
if (n < 64) { m-=1; n <<= 3; }
if (n < 64) { m-=1; n <<= 3; }
if (n < 64) { m-=1; n <<= 3; }
oval[5]='0'+(n&7);
oval[4]='0'+((n>>3)&7);
oval[3]='0'+((n>>6)&7);
write( STDOUT_FILENO, oval, m );
}
int main(void) {
char buffer[255];
int count=1;
int i;
while (count>0) {
count=read( STDIN_FILENO, buffer, 17 );
if (count>0) write( STDOUT_FILENO, "echo -ne $'",11 );
for (i=0; i<count; ++i) oct( buffer[i] );
if (count>0) write( STDOUT_FILENO, "'\n", 2 );
}
write( STDOUT_FILENO,"#\n",2);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
一旦 mips 的 octdump 在调制解调器上保存(chmod +x)为 /var/octdump,它就可以正常运行。(发挥你的想象力,想象一下我是如何把它放在那里的……Dlink 的 TFTP,朋友们都坏了。)
我能够使用 octdump 从 DSL 调制解调器复制所有动态库并检查它们,使用自动脚本来避免手动复制/粘贴。
#!/bin/env python
# octget.py
# A program to upload a file off an embedded linux device via telnet
import socket
import time
import sys
import string
if len( sys.argv ) != 4 :
raise ValueError, "Usage: octget.py IP_OF_MODEM passwd path_to_file_to_get"
o = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
o.connect((sys.argv[1],23)) # The IP address of the DSL modem.
time.sleep(1)
sys.stderr.write( o.recv(1024) )
o.send("admin\r\n");
time.sleep(0.1)
sys.stderr.write( o.recv(1024) )
o.send(sys.argv[2]+"\r\n")
time.sleep(0.1)
o.send("sh\r\n")
time.sleep(0.1)
sys.stderr.write( o.recv(1024) )
o.send("cd /var\r\n")
time.sleep(0.1)
sys.stderr.write( o.recv(1024) )
o.send("./octdump.x < "+sys.argv[3]+"\r\n" );
sys.stderr.write( o.recv(21) )
get="y"
while get and not ('#' in get):
get = o.recv(4096)
get = get.translate( None, '\r' )
sys.stdout.write( get )
time.sleep(0.5)
o.close()
Run Code Online (Sandbox Code Playgroud)
DSL520B 调制解调器具有以下库... libcrypt.so.0 libpsi.so libutil.so.0 ld-uClibc.so.0 libc.so.0 libdl.so.0 libpsixml.so
...我想我可以使用这些库进行交叉编译,因为(至少在理论上)——GCC 可以链接它们;我的问题可能会得到解决。
我非常确定从 gcc-4.9.0/mips-linux/mips-linux/lib 中删除所有不兼容的 .so 库,但保留通用的 crt..o 文件;然后我将调制解调器的库复制到交叉编译器目录中。
但是,即使源代码的内核版本和调制解调器的内核版本匹配,GCC 在 crt 文件中发现了未定义的符号......因此,通用 crt 文件或调制解调器库本身都存在某种缺陷...... ..我不知道为什么。不知道如何获取完整的库版本?加州大学图书馆?库,我不确定如何获得正确的源代码来从头开始重新编译库和 crt。