如何为嵌入式设备获得正确的MIPS libc工具链

时间:2014-10-09 07:47:58

标签: linux gcc embedded cross-compiling libc

我遇到了各种公司的问题(重复)。嵌入式Linux产品,其中的GPL源代码与系统上实际运行的内容不匹配。它关闭了#34;但并不完全正确,特别是对于他们使用的标准C库。 是不是违反了GPL?

这种不匹配通常导致程序员(像我一样)交叉编译,只是让设备以密码方式回复"找不到文件"程序运行时类似的东西。

我并不是唯一遇到这种问题的人 - 对于许多人而言,线程直接和间接地与问题相关:例如: Compile parameters for MIPS based codesourcery toolchain?

我在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
  ....

注意: 这篇文章的其余部分是一个博客,展示了我如何提出上述问题,并在一个地方提供有关该主题的有用信息。 不要费心阅读,除非你想知道我实际上已经研究了这个问题......在血淋淋的细节......以及如何不回答我的问题。

在(例如)D-link调制解调器上运行libc程序的正确(理论)方法只是从制造商处获取产品的 TRUE 源代码,并编译反对那些图书馆....(它是GPL!?对,所以法律就在我们这边,对吗?)

例如:我刚刚购买了D-Link DSL-520B调制解调器和526B调制解调器 - 但是在制造商"忘记"之后发现了这一点。为520B提供linux源代码,但确实有526B。我在线查看了所有DSL-5xxB设备的源代码和信息。工具链,我很高兴所有这些(包括526B) - 包含SAME预编译的libc.so.0,MD5sum为6ed709113ce615e9f170aafa0eac04a6。所以从理论上讲,DSL-5xxB系列中所有受支持的调制解调器似乎都使用相同的libc库...我希望我可以使用该库。

但是在我弄清楚如何让DSL调制解调器本身向我发送已安装的/lib/libc.so.0库的副本之后 - 我发现他们厌恶他们都使用了一个带有MD5总和的库b8d492decc8207e724a0822641205078 。在我购买的调制解调器(支持与否)中,找到了源代码工具链中包含的相同库。

为了验证来自D-link的工具链是否有缺陷,我没有编译程序(因为它是错误的二进制格式,工具链无法在我的电脑上运行) - 但我找到了toolchain已经有一些预先编译的mips二进制文件;所以我只是将一个下载到调制解调器和chmod + x - 并且(惊讶)我收到了消息"找不到文件。"当我试图运行它时......它不会运行。

所以,我知道工具链并不是立刻好的,但不完全是为什么。

我决定推出MIPS GCC(二进制版)的新版本,它应该具有更少的错误,更多功能,并且在大多数PC平台上都支持。这是GO的方式!

请参阅:Kernel.org pre-compiled binaries

在选择较旧的" mips"之后,我升级到了gcc 4.9.0。从上面的网站获取正确的FTP页面;并设置我的贝壳'一旦安装了交叉编译器的/ bin目录的PATH变量。

然后我将所有头文件和库从D-link源代码包复制到新的交叉编译器中,以验证它是否可以编译D-link libc二进制文件。它在第一次尝试时就做了,编译了#hello world!"没有警告或错误进入mips 32大端二进制文件。

开始编辑:)@ChrisStratton在评论中指出(在这篇文章之后)我对工具链的测试是不充分的,并且使用较旧的GCC和较旧的库 - 尽管它正确链接 - 作为测试有缺陷。我希望有一种方法可以给他点评他的意见 - 我已经确信他是对的;虽然这使得D-link甚至做出了更糟糕的问题 - 因为没有办法从调制解调器上的二进制文件中知道他们实际使用的GCC。用于内核的GCC与用户空间中使用的GCC不一样。

为了测试新编译器与调制解调器的兼容性并制作工具,我可以获得调制解调器上找到的实际库的副本:( END EDIT )我写的一个根本没有使用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                          

...和...

// 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;                                                                   
}                     

一旦mips&#39; 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()  

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文件或调制解调器库本身都是某种方式有缺陷......而且我不知道为什么。不知道如何获得完整的库版本? ucLibc?库,我不知道如何从头开始重新编译库和crt的CORRECT源代码。

0 个答案:

没有答案