如何创建静态链接共享库 [英] How to create static linked shared libraries

查看:36
本文介绍了如何创建静态链接共享库的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于我的硕士论文,我正在尝试为 ARM Cortex-M3 嵌入式系统采用共享库方法.由于我们的目标板没有 MMU,我认为使用普通"动态共享库是没有意义的.因为 .text 是直接从闪存执行的,而 .data 是在启动时复制到 RAM 中的,所以我无法解决与代码相关的 .data 问题,因此 GOT 也是如此.GOT 必须通过一个必须在链接时定义的绝对地址来访问.那么为什么不在链接时为所​​有符号分配固定的绝对地址......?

For my master's thesis i'm trying to adapt a shared library approach for an ARM Cortex-M3 embedded system. As our targeted board has no MMU I think that it would make no sense to use "normal" dynamic shared libraries. Because .text is executed directly from flash and .data is copied to RAM at boot time I can't address .data relative to the code thus GOT too. GOT would have to be accessed through an absolute address which has to be defined at link time. So why not assigning fixed absolute addresses to all symbols at link time...?

"Linkers and Loaders" 一书中我了解到静态链接共享库,即库中的程序和数据地址在链接时绑定到可执行文件的库".链接的章节描述了如何创建此类库,并提供了对 Unix System V、BSD/OS 的参考;但也提到了 Linux 和它的 uselib() 系统调用.不幸的是,这本书没有提供如何实际创建此类库的信息,例如工具和/或编译器/链接器开关.除了那本书之外,我几乎找不到关于野外"此类图书馆的任何其他信息.我在这方面发现的唯一内容是 Linux 的 prelink.但由于这对普通"动态库进行操作,这并不是我真正要搜索的内容.

From the book "Linkers and Loaders" I got aware of "static linked shared libraries, that is, libraries where program and data addresses in libraries are bound to executables at link time". The linked chapter describes how such libraries could be created in general and gives references to Unix System V, BSD/OS; but also mentions Linux and it's uselib() system call. Unfortunately the book gives no information how to actually create such libraries such as tools and/or compiler/linker switches. Apart from that book I hardly found any other information about such libraries "in the wild". The only thing I found in this regard was prelink for Linux. But as this operates on "normal" dynamic libraries thats not really what I'm searching for.

我担心这些库的使用非常具体,因此不存在创建它们的通用工具.尽管在此上下文中提到的 uselib() 系统调用让我感到疑惑.但我想确保在开始破解我自己的链接器之前我没有忽略任何东西...... ;) 那么谁能给我更多关于这些库的信息?

I fear that the use of these kind of libaries is very specific, so that no common tools exists to create them. Although the mentioned uselib() syscall in this context makes me wondering. But I wanted to make sure that I haven't overlooked anything before starting to hack my own linker... ;) So could anyone give me more information about such libraries?

此外,我想知道是否有任何 gcc/ld 开关可以链接和重定位文件,但将重定位条目保留在文件中 - 以便可以重新定位?我找到了-r"选项,但这完全跳过了重定位过程.有人有想法吗?

Furthermore I'm wondering if there is any gcc/ld switch which links and relocates a file but keeps the relocation entries in the file - so that it could be re-relocated? I found the "-r" option, but that completely skips the relocation process. Does anyone have an idea?

是的,我也知道链接器脚本.使用 gcc libfoo.c -o libfoo -nostdlib -e initLib -Ttext 0xdeadc0de 我设法获得了某种链接 &重定位的目标文件.但到目前为止,我还没有发现任何可能将主程序与此链接并将其用作共享库.(链接动态共享库的正常方式"将被链接器拒绝.)

Yes, I'm also aware of linker scripts. With gcc libfoo.c -o libfoo -nostdlib -e initLib -Ttext 0xdeadc0de I managed to get some sort of linked & relocated object file. But so far I haven't found any possibility to link a main program against this and use it as shared library. (The "normal way" of linking a dynamic shared library will be refused by the linker.)

推荐答案

概念

这样一个共享库的最小概念.

Concepts

Minimum concept of what such a shared library maybe about.

  • 相同的代码
  • 不同的数据

在这方面有变化.您是否支持库之间的链接.引用是 DAG 结构还是完全循环的?您想将代码放入ROM,还是支持代码更新?您是否希望在 进程 最初运行后加载库?最后一个一般是静态共享库动态共享库的区别.虽然很多人也会禁止库之间的引用.

There are variations on this. Do you support linking between libraries. Are the references a DAG structure or fully cyclic? Do you want to put the code in ROM, or support code updates? Do you wish to load libraries after a process is initially run? The last one is generally the difference between static shared libraries and dynamic shared libraries. Although many people will forbid references between libraries as well.

最终,一切都将归结为处理器的寻址模式.在这种情况下,ARM 拇指.loader 通常与操作系统和使用的二进制格式耦合.您的工具链(编译器和链接器)还必须支持二进制格式,并且可以生成所需的代码.

Eventually, everything will come down to the addressing modes of the processor. In this case, the ARM thumb. The loader is generally coupled to the OS and the binary format in use. Your tool chain (compiler and linker) must also support the binary format and can generate the needed code.

支持通过寄存器访问数据是 APCS(ARM 过程调用标准).在这种情况下,数据通过sb(用于静态基)寄存器R9 访问.静态基础堆栈检查是可选功能.我相信您可能需要配置/编译 GCC 来启用或禁用这些选项.

Support for accessing data via a register is intrinsic in the APCS (the ARM Procedure calling standard). In this case, the data is accessed via the sb (for static base) which is register R9. The static base and stack checking are optional features. I believe you may need to configure/compile GCC to enable or disable these options.

选项 -msingle-pic-base-mpic-register 位于 GCC 手册.这个想法是操作系统最初会为每个库用户分配单独的数据,然后在上下文切换时加载/重新加载 sb.当代码运行到库时,数据通过 sb 访问该实例数据.

The options -msingle-pic-base and -mpic-register are in the GCC manual. The idea is that an OS will initially allocate separate data for each library user and then load/reload the sb on a context switch. When code runs to a library, the data is accessed via the sb for that instances data.

Gcc 的 arm.c 代码 具有 require_pic_register(),它为共享库中的数据引用生成代码.它可能对应于 ARM ATPCS 共享库 力学.见第 5.5 节

Gcc's arm.c code has the require_pic_register() which does code generation for data references in a shared library. It may correspond to the ARM ATPCS shared library mechanics.See Sec 5.5

您可以通过使用宏和内联汇编程序以及可能的函数注释来绕过工具链,例如 nakedsection.但是,在这种情况下,库和流程可能需要修改代码;即,非标准宏,如 EXPORT(myFunction)

You may circumvent the tool chain by using macros and inline assembler and possibly function annotations, like naked and section. However, the library and possibly the process need code modification in this case; Ie, non-standard macros like EXPORT(myFunction), etc.

如果系统是完全指定的(一个 ROM 映像),您可以制作偏移量,您可以预先生成系统中每个库唯一的数据偏移量.这可以通过链接描述文件轻松完成.使用 NOLOAD 并将库数据放在一些虚假部分.甚至可以使主程序成为静态共享库.例如,您正在制作具有四个以太网端口的网络设备.主应用程序处理一个端口上的流量.您可以使用不同的数据生成应用程序的四个实例,以指示正在处理的端口.

If the system is fully specified (a ROM image), you can make the offsets you can pre-generate data offsets that are unique for each library in the system. This is done fairly easily with a linker script. Use the NOLOAD and put the library data in some phony section. It is even possible to make the main program a static shared library. For instance, you are making a network device with four Ethernet ports. The main application handles traffic on one port. You can spawn four instances of the application with different data to indicate which port is being handled.

如果您有大量的库类型混合/匹配,库数据的占用空间可能会变大.在这种情况下,当通过外部 API 上的包装函数调用库时,您需要重新调整 sb.

If you have a large mix/match of library types, the foot print for the library data may become large. In this case you need to re-adjust the sb when calls are made through a wrapper function on the external API to the library.

  void *__wrap_malloc(size_t size)  /* Wrapped version. */
  {
       /* Locals on stack */
       unsigned int new_sb = glob_libc; /* accessed via current sb. */
       void * rval;
       unsigned int old_sb;

       volatile asm(" mov %0, sb\n" : "=r" (old_sb);
       volatile asm(" mov sb, %0\n" :: "r" (new_sb);
       rval = __real_malloc(size);
       volatile asm(" mov sb, %0\n" :: "r" (old_sb);
       return rval;
  }

请参阅 GNU ld --wrap 选项.如果您有一组更大的同质库,则需要这种复杂性.如果您的库仅包含libc/libsupc++",那么您可能不需要包装任何东西.

See the GNU ld --wrap option. This complexity is needed if you have a larger homogenous set of libraries. If your libraries consists of only 'libc/libsupc++', then you may not need to wrap anything.

ARM ATPCS 有贴面由执行等效操作的编译器插入,

The ARM ATPCS has veneers inserted by the compiler that do the equivalent,

LDR a4, [PC, #4] ; data address
MOV SB, a4
LDR a4, [PC, #4] ; function-entry
BX a4
DCD data-address
DCD function-entry

使用此技术的库数据大小为 4k(可能为 8k,但可能需要编译器修改).限制是通过 ldr rN, [sb, #offset],ARM 限制偏移为 12 位.使用包装,每个库都有 4k 的限制.

The size of the library data using this technique is 4k (possibly 8k, but that might need compiler modification). The limit is via ldr rN, [sb, #offset], were ARM limits offset to 12bits. Using the wrapping, each library has a 4k limit.

如果您有多个在原始应用程序构建时未知的库,那么您需要将每个库包装起来,并通过 OS 加载程序将 GOT 类型表放置在主应用程序静态库中的固定位置.每个应用程序都需要为每个库提供一个指针的空间.如果应用程序不使用该库,则操作系统不需要分配空间,并且该指针可以为 NULL.

If you have multiple libraries that are not known when the original application builds, then you need to wrap each one and place a GOT type table via the OS loader at a fixed location in the main applications static base. Each application will require space for a pointer for each library. If the library is not used by the application, then the OS does not need to allocate the space and that pointer can be NULL.

库表可以通过.text中的已知位置、原始进程sb或通过堆栈掩码访问.例如,如果所有进程都获得一个 2K 的堆栈,则可以为库表保留低 16 个字.sp &~0x7ff 将为所有任务提供一个隐式锚点.操作系统也需要分配任务堆栈.

The library table can be accessed via known locations in .text, via the original processes sb or via a mask of the stack. For instance, if all processes get a 2K stack, you can reserve the lower 16 words for a library table. sp & ~0x7ff will give an implicit anchor for all tasks. The OS will need to allocate task stacks as well.

注意,这种机制与 ATPCS 不同,ATPCS 使用 sb 作为表来获取实际库数据的偏移量.由于所描述的 Cortex-M3 的内存相当有限,因此每个单独的库不太可能需要使用超过 4k 的数据.如果系统支持分配器,则可以解决此限制.

Note, this mechanism is different than the ATPCS, which uses sb as a table to get offsets to the actual library data. As the memory is rather limited for the Cortex-M3 described it is unlikely that each individual library will need to use more than 4k of data. If the system supports an allocator this is a work around to this limitation.

参考资料

  • Xflat technical overview - Technical discussion from the Xflat authors; Xflat is a uCLinux binary format that supports shared libraries. A very good read.
  • Linkage table and GOT - SO on PLT and GOT.
  • ARM EABI - The normal ARM binary format.
  • Assemblers and Loader, by David Solomon. Especially, pg262 A.3 Base Registers
  • ARM ATPCS, especially Section 5.5, Shared Libraries, pg18.
  • bFLT is another uCLinux binary format that supports shared libraries.

这篇关于如何创建静态链接共享库的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆