Просто для полной ясности: инструкция CALL
помещает адрес следующей за ней инструкции в стек и переходит на целевой адрес. Это означает, что
x: call start
y:
морально эквивалентно (игнорируя то, что мы хвалим %rax
здесь):
x: lea y(%rip), %rax
push %rax
jmp start
y:
И наоборот RET
извлекает адрес из стека и переходит к нему.
Теперь в своем коде вы делаете popq %rsi
, а затем ret
возвращается к тому, что вас называло. Если вы просто измените popq
на lea str(%rip), %rsi
, чтобы загрузить %rsi
с адресом str
, у вас все еще будет возвращаемое значение (адрес str
) в стеке! Чтобы исправить свой код, просто вручную извлеките возвращаемое значение из стека (add $8, %rsp
) ИЛИ более разумно переместитесь на str
после функции, чтобы вам не нужен неуклюжий вызов.
Обновлено с полным автономным примером:
# p.s
#
# Compile using:
# gcc -c -fPIC -o p.o p.s
# gcc -fPIC -nostdlib -o p -Wl,-estart p.o
.text
.global start # So we can use it as an entry point
start:
movq $1, %rax #sys_write
movq $1, %rdi
lea str(%rip), %rsi
movq $5, %rdx
syscall
mov $60, %rax #sys_exit
mov $0, %rdi
syscall
.data
str:
.string "test\n"
Разборка кода с помощью objdump -d p
показывает, что код действительно не зависит от позиции, даже при использовании .data
.
p: file format elf64-x86-64
Disassembly of section .text:
000000000040010c <start>:
40010c: 48 c7 c0 01 00 00 00 mov $0x1,%rax
400113: 48 c7 c7 01 00 00 00 mov $0x1,%rdi
40011a: 48 8d 35 1b 00 20 00 lea 0x20001b(%rip),%rsi # 60013c <str>
400121: 48 c7 c2 05 00 00 00 mov $0x5,%rdx
400128: 0f 05 syscall
40012a: 48 c7 c0 3c 00 00 00 mov $0x3c,%rax
400131: 48 c7 c7 00 00 00 00 mov $0x0,%rdi
400138: 0f 05 syscall