如何在不使用gcc的情况下将使用C标准库的气体汇编程序与ld链接?
作为更准确地了解 C 程序如何工作以及程序能够使用 libc 必须存在的最低内容级别的练习,我自己尝试主要使用 Gas 和 ld 在 x86 汇编中进行编程。
作为一个有趣的小挑战,我成功地组装并链接了几个链接到不同自制动态库的程序,但我无法从头开始编写程序以使用 libc 函数调用而不直接使用 gcc。
我了解各个 C 库函数的调用约定,并通过使用 objdump 和 readelf 彻底检查了 gcc 编译的程序,但还没有了解在 Gas 汇编文件中包含哪些信息以及要调用哪些参数在 ld 中成功链接到 libc。有人对此有任何见解吗?
我在 x86 机器上运行 Linux。
As an exercise to learn more precisely how c programs work and what minimum level of content must exist for a program to be able to use libc, I've taken it upon myself to attempt to program primarily in x86 assembly using gas and ld.
As a fun little challenge, I've successfully assembled and linked several programs linked to different self-made dynamic libraries, but I have failed to be able to code a program from scratch to use libc function calls without directly using gcc.
I understand the calling conventions of individual c library functions, and have thoroughly inspected programs compiled out of gcc through use of objdump and readelf, but haven't gotten anywhere as far as what information to include in a gas assembly file and what parameters to invoke in ld to successfully link to libc. Anyone have any insight to this?
I'm running Linux, on an x86 machine.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
要成功使用带有动态链接的 libc,至少需要做三件事:
/usr/lib/crt1.o
,其中包含_start
,这将是ELF 二进制文件的入口点;/usr/lib/crti.o
(libc之前)和/usr/lib/crtn.o
(之后),它们提供了一些初始化和终止代码;/lib/ld-linux.so
。例如:
There are at least three things that you need to do to successfully use libc with dynamic linking:
/usr/lib/crt1.o
, which contains_start
, which will be the entry point for the ELF binary;/usr/lib/crti.o
(before libc) and/usr/lib/crtn.o
(after), which provide some initialisation and finalisation code;/lib/ld-linux.so
.For example:
如果您在汇编中定义
main
,Matthew 的回答很好地告诉了您最低要求。
让我向您展示如何在系统中找到这些路径。运行:
然后拿起马修提到的文件。
gcc -v
为您提供 GCC 使用的确切链接器命令。collect2 是 GCC 用作链接器前端的内部可执行文件-end,其接口与
ld
类似。在 Ubuntu 14.04 64 位 (GCC 4.8) 中,我最终得到:
您可能还需要
-lgcc
和-lgcc_s
。另请参阅:我真的需要 libgcc 吗?如果您定义
汇编中的_start
如果我定义了
_start
,则 glibc 的 hello world 只能使用:我不确定这是否可靠,即
crt 是否可靠
可以安全地跳过初始化来调用 glibc 函数。另请参阅:为什么汇编程序只能在与 crt1.o、crti.o 和 crtn.o 链接时才能工作?If you define
main
in assemblyMatthew's answer does a great job of telling you the minimum requirements.
Let me show you how how to find those paths in your system. Run:
and then pick up the files Matthew mentioned.
gcc -v
gives you the exact linker command GCC uses.collect2 is the internal executable GCC uses as a linker front-end, which has a similar interface to
ld
.In Ubuntu 14.04 64-bit (GCC 4.8), I ended up with:
You might also need
-lgcc
and-lgcc_s
. See also: Do I really need libgcc?If you define
_start
in assemblyIf I defined the
_start
, the hello world from glibc worked with just:I'm not sure if this is robust, i.e. if the
crt
initializations can be safely skipped to invoke glibc functions. See also: Why does an assembly program only work when linked with crt1.o crti.o and crtn.o?我认为这样的事情应该可行:
I think something like this should work:
如果您确实使用
_start
而不是main
(如上面的一些注释中提到的),您还需要更改程序退出的方式,否则您将出现段错误:在 Kubuntu 18.04.2 (gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0) 上:
另外,找出系统上动态链接器的一种简单方法是编译一个小 C 程序然后在二进制文件上运行 ldd:
test.c:
针对可执行文件编译并运行 ldd:
If you do use
_start
instead ofmain
(as mentioned in some of the comments above), you'll also need to change the way the program exits, or you'll get a seg fault:On Kubuntu 18.04.2 (gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0):
Also, one easy way to find out what the dynamic linker is on your system is to compile a small C program and then run
ldd
on the binary:test.c:
Compile and run ldd against executable: