我一直在逐步学习《汇编语言:第三版》 ,并且在最后一章“前往C”中。我正在尝试获得一种一致的方法来转换32位代码,该代码在我的64位Ubuntu系统上调用C库(glibc)函数puts
。 (我想阅读文本的最后50页,大概是C的更深层次的内容,但使用的是用32位代码编写的汇编基础)。代码是:
SECTION .data ; Section containing initialised data
EatMsg: db "Eat at Joe's!",0
SECTION .text ; Section containing code
extern puts ; Simple "put string" routine from clib
global main ; Required so linker can find entry point
main:
push ebp ; Set up stack frame for debugger
mov ebp,esp
push ebx ; Must preserve ebp, ebx, esi, & edi
push esi
push edi
;;; Everything before this is boilerplate; use it for all ordinary apps!
push EatMsg ; Push address of message on the stack
call puts ; Call clib function for displaying strings
add esp,4 ; Clean stack by adjusting ESP back 4 bytes
;;; Everything after this is boilerplate; use it for all ordinary apps!
pop edi ; Restore saved registers
pop esi
pop ebx
mov esp,ebp ; Destroy stack frame before returning
pop ebp
ret ; Return control to Linux
建议的nasm和链接器命令为
nasm -f elf -g -F stabs eatclib.asm
gcc eatclib.o -o eatclib
与我找到的解决方案最接近的近似值是:Call C functions from 64-bit assembly。
我尝试将扩展寄存器转换为rbp
,rsp
等;在调用puts
之后,将堆栈指针调整为8位而不是4位,并使用以下方法调整Makefile:
nasm -f elf64 -g -F dwarf eatclib.asm
和
gcc eatclib.o -o eatclib -m64 -static
但出现了细分错误。
我对C调用约定的理解仍然模糊不清/微不足道,以至于当我尝试跟着gdb调试器一起时,我并没有真正深入地尝试寻找错误(问题都只是有些熟悉32位约定,而对于C而言则不多)。本书旨在成为几乎没有C背景的新手汇编程序员的入门书籍。
从另一个方向尝试,一个简单的C程序使用带字符串的puts会产生以下文件(使用gcc -S
选项):
.file "SayHello.c"
.text
.section .rodata
.align 8
.LC0:
.string "This is based on an example from C Primer Plus"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
call puts@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
这里运行了已编译的代码(而且我了解其中的大部分内容,除了.cfi
指令,.rodata
所表示的含义以及为什么gas @PLT
会将其puts
粘在ld -dynamic-linker /lib/ld-linux.so.2 -o eatclib -lc eatclib.o
上。 )这当然是gas语法,我主要使用的文字是NASM。
我还尝试过使用装载程序代替gcc,并且在 Professional Assembly Language (由Richard Blum撰写)的第89页上找到了一行
ld: i386 architecture of input file `eatclib.o' is incompatible with i386:x86-64 output
ld: warning: cannot find entry symbol _start; defaulting to 0000000000400250
makefile:2: recipe for target 'eatclib' failed
但最终会出现与我之前遇到的非常典型的链接器错误:
-m32
我也尝试将apt-get
选项传递给链接器也无济于事。
无论如何,我正在寻找可行的建议。在我的搜索中,我看到了一些示例,其中有人建议使用-melf_i386
并安装新的(实际上是旧的)库,但是这些库似乎有效地破坏了整个64位的东西系统-当我能够使用传递到链接器的{% block moteurs_body %}
<p></p>
{% set boucle = 6 %}
<div class="container">
{% for moteur in moteurs %}
{% if (loop.index % (6*6) == 0) and not loop.first %}<div class="newPage"></div>{% endif %}
{% if loop.first %}
<div class="row">
{% endif %}
{% if boucle == 0 %}
</div>
<div class="row">
{% set boucle = 6 %}
{% endif %}
<div class="col-2">
<div class="card">
<img class="card-img-top" src="../../{{ moteur.urlQRCode }}" alt="Mince, pas d'image...">
<div class="card-body">
<p class="card-text text-center">
{{ moteur.numeroMoteur }}
</p>
</div>
</div>
</div>
{% set boucle = (boucle - 1) %}
{% if loop.last %}
</div>
{% endif %}
{% endfor %}
</div>
{% endblock %}
选项运行先前的32位代码。
答案 0 :(得分:3)
要汇编和链接使用libc的64位nasm代码,请输入:
nasm -f elf64 program.asm
gcc -o program program.o
根据您的系统和编程风格,您可能需要将-no-pie
传递给gcc
,以便它接受与位置有关的代码。
不建议在libc中进行链接时直接调用链接器,因为没有稳定的方法可以手动提取C运行时初始化代码。仅将-lc
传递给链接器不足以使libc正常工作。
请注意elf64
,以使nasm发出64位目标文件。除非另有说明,否则gcc可以在64位平台上使用64位代码,因此不需要其他选项。您可能要添加调试符号,但请记住,刺是一种过时的格式。您可能想要这样:
nasm -f elf64 -gdwarf program.asm
机械转换源代码或多或少是可能的。请记住以下差异:
rax
,rcx
,rdx
,rbx
,rsp
,rbp
,{{1 }},
和rsi
。
rdi
至r8
。它们的32位,16位和8位版本分别称为r15
,r8d
,r8b`等。r8w
,rdi
,rsi
,rdx
,rcx
和{{1}中从左向右传递}。被叫方必须保留寄存器r8
,r9
,rbx
,rbp
,rsp
,r12
和r13
,所有其他通用寄存器均可自由覆盖。浮点参数在SSE寄存器中传递并返回。如果参数过多,则会在堆栈上传递额外的参数。r14
指令压入8个字节,而函数序言中的r15
指令压入另外8个字节,因此默认情况下会是这种情况,除非您手动在堆栈上分配空间。只需记住以16个字节为增量进行操作即可。这是您问题中的代码,已转换为64位代码。所有更改均已标记:
call
请注意,我遗漏了许多样板。 push rbp
用于加载 SECTION .data
EatMsg: db "Eat at Joe's!",0
SECTION .text
extern puts
global main
main: ; function entry (stack alignment: 16 bytes + 8 bytes)
push rbp ; setup...
mov rbp, rsp ; the stack frame (stack now aligned to 16 bytes + 0 bytes)
; since we have so many registers, I only preserve those
; I want to use and that must be preserved, of which there
; are none in this program.
lea rdi, [rel EatMsg] ; load address of EatMsg into rdi
call puts ; call puts
; no cleanup needed as we have not pushed anything
pop rbp ; restore rbp
ret ; return
的地址,而不是简单的lea
,因此您的程序与位置无关。如果您不知道这意味着什么,则可以放心地忽略此花絮,直到以后。
最后,您通常可以忽略cfi指令。它们添加用于异常处理的元数据,这仅在您的代码调用引发异常的C ++函数时才重要。它们不会更改代码本身的行为。
答案 1 :(得分:2)
Jester建议安装gcc-multilib,然后使用与32位代码一起使用的gcc -m32参数。 (这肯定是stackoverflow上其他任何地方的副本...昨天在某处看到了该建议,但不信任它似乎需要进行的gcc大修。)