【问题标题】:Assembler output does not run on my Linux machine汇编器输出无法在我的 Linux 机器上运行
【发布时间】:2019-11-07 12:31:22
【问题描述】:

我跟进this页面,编译如下代码

; assembly program that calls a C function on 64-bit Linux
;
;    int main(void) {
;       printf(fmt, 1, msg1);
;       printf(fmt, 2, msg2);
;       return 0;
;
; Assemble in 64-bit:   nasm  -f elf64 -o hp64.o -l hp64.lst  hello-printf-64.asm
;
; Link:         ld hp64.o  -o hp64  -lc  --dynamic-linker /lib/ld-2.7.so
;   or maybe    ld hp64.o  -o hp64  -lc  --dynamic-linker /lib/ld-linux-x86-64.so.2
;       (the "-lc" option is needed to resolve "printf")
;---------------------------------------
    section .data
fmt     db "%u  %s",10,0
msg1    db "Hello",0
msg2    db "Goodbye",0

    section .text
    extern printf
    global _start

_start:
    mov  edx, msg1
    mov  esi, 1
    mov  edi, fmt
    mov  eax, 0     ; no f.p. args
    call printf

    mov  edx, msg2
    mov  esi, 2
    mov  edi, fmt
    mov  eax, 0     ; no f.p. args
    call printf

    mov  ebx, 0     ; return value
    mov  eax, 1
    int  0x80

通过

nasm  -f elf64 -o hp64.o -l hp64.lst  hello-printf-64.asm
ld hp64.o  -o hp64A  -lc  --dynamic-linker /lib/ld-2.7.so
ld hp64.o  -o hp64B  -lc  --dynamic-linker /lib/ld-linux-x86-64.so.2

没有一个可执行文件 hp64Ahp64B 可以运行。

$ ./hp64A
bash: ./hp64A: No such file or directory
$ ./hp64B
bash: ./hp64B: No such file or directory

虽然两者都是可执行文件。

$ ll
total 30
drwxrwxrwx 1 ar2015 ar2015 4096 Nov  7 23:23 ./
drwxrwxrwx 1 ar2015 ar2015 4096 Nov  7 22:46 ../
-rwxrwxrwx 1 ar2015 ar2015  928 Nov  7 22:47 hello-printf-64.asm*
-rwxrwxrwx 1 ar2015 ar2015 2960 Nov  7 23:21 hp64A*
-rwxrwxrwx 1 ar2015 ar2015 2976 Nov  7 23:21 hp64B*
-rwxrwxrwx 1 ar2015 ar2015 2448 Nov  7 23:21 hp64.lst*
-rwxrwxrwx 1 ar2015 ar2015 1104 Nov  7 23:21 hp64.o*

我的机器是

$ uname -a
Linux ar2015co 4.15.0-66-generic #75~16.04.1-Ubuntu SMP Tue Oct 1 14:01:08 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

如何运行这些可执行文件?

【问题讨论】:

  • 大概你的动态链接器不在给定的位置。您应该使用main 作为入口点而不是_startgcc 进行链接。也不要使用退出系统调用,只使用retcall exit。此外,避免在 64 位代码中使用 int 0x80(尽管它适用于此处的退出系统调用)。如果你坚持,找到动态链接器的正确位置,很可能是/lib64/ld-linux-x86-64.so.2
  • 我关注了--dynamic-linker /lib64/ld-linux-x86-64.so.2 并使用了gcc。两者都按照您的建议工作。
  • @Jester,在ldgcc 之间哪个更好?看起来ld 创建的文件更小了。
  • 如果您使用的是 C 库,最好的选择是 gcc,因为它会做所有正确的事情。
  • 100% 同意,如果您想制作动态可执行文件,请使用 GCC,特别是如果您想链接任何库。您的发行版使用正确的 ld.so 路径等对其进行配置。 gcc -nostartfiles 省略 CRT(因此您可以编写 _start),gcc -nostdlib 省略 CRT 和标准库,但仍然可以使 PIE 可执行。

标签: linux assembly x86-64 nasm dynamic-linking


【解决方案1】:

您可能有错误的动态链接器路径。

如果您想要制作动态可执行文件,尤其是想要链接任何库时,请使用 GCC。您的发行版使用正确的 ld.so 路径等对其进行配置。 (使用gcc -v 查看正确的路径,和/或ldd ./a.outreadelf 打印系统上工作可执行文件的ELF 解释器路径。)

  • gcc main.o 链接正常,包括 CRT 启动、libc 和 libgcc 辅助函数。

  • gcc -no-pie -nostartfiles start.o 省略 CRT(所以你可以写一个_start)。在 Linux 上,这确实有效; glibc 安排通过动态链接器挂钩调用它的 init 函数,因此如果您知道自己在做什么,它就可以工作。

    不过,一般而言,如果您想使用 libc 函数,请编写一个由 CRT 启动代码调用的 main

    我在这里使用了-no-pie,因为您的代码使用mov r32, imm32 优化将静态地址放入寄存器;它取决于非 PIE 默认代码模型。 (否则使用 RIP 相对 LEA)。

  • gcc -nostdlib 省略 CRT 和标准库。之后,您还可以手动将一些库添加到 GCC 命令行中。 (如果 -pie 是 GCC 的默认值,即使没有库,这仍然会生成 PIE。static-pie is a separate thing not on by default。)

  • gcc -nostdlib -static 生成一个普通的静态 ELF 可执行文件(暗示 -no-pie),就像你从 ld 得到的一样

如果您从其中任何一个中省略 -no-pie-static 除外),如果您的 GCC 配置为 --enable-default-pie,您将获得一个 PIE,就像过去几年大多数发行版所做的那样。 (32-bit absolute addresses no longer allowed in x86-64 Linux?) 另见What's the difference between "statically linked" and "not a dynamic executable" from Linux ldd? re: static-pie

同样在 PIE 可执行文件中,您需要 call library functions through the PLT or via their GOT entry,例如 call [printf wrt.. got]


`int  0x80`

不要这样做有两个原因:

  1. 旧版 32 位 ABI:What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?
  2. 在使用 stdio 函数打印后不要使用原始的 _exit 系统调用。 如果通过管道输出,stdout 将被全缓冲,并且当 sys_exit 退出而不刷新 stdio 缓冲区时输出将丢失第一的。从maincall sys_exit 返回。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-10-03
    • 1970-01-01
    • 2020-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    相关资源
    最近更新 更多