简体   繁体   English

如何在 x86 中将字符串压入堆栈(AT&T 语法)

[英]how to push a string into a stack in x86 (AT&T syntax)

In x86 AT&T synatax, how does one push an ASCII string into a stack?在 x86 AT&T 语法中,如何将 ASCII 字符串压入堆栈? (Or how does a C program push an ASCII string into a stack?) (或者 C 程序如何将 ASCII 字符串压入堆栈?)

My intial attempt was to create data section, use .asciz and then push things using LEA and MOV using displacement.我最初的尝试是创建数据部分,使用 .asciz,然后使用 LEA 和 MOV 使用位移推送东西。 But it all ended up with errors.但这一切都以错误告终。

C programs don't push strings, they push pointers to strings. C 程序不推送字符串,它们推送指向字符串的指针。

Asm programs can push variable-length things onto the stack, but it's weird and rarely useful. Asm 程序可以将可变长度的东西压入堆栈,但它很奇怪而且很少有用。 When optimizing your code, only consider it after considering the normal way.在优化你的代码时,只有考虑正常方式后才考虑它。

It could certainly be useful for allocating space for a variable-sized input that's only available one byte at a time, like reading input.它对于为一次只能使用一个字节的可变大小输入分配空间当然很有用,例如读取输入。 (Like a local variable-sized array, except you grow it as you read input, instead of having to choose a size before you're done reading.) (就像一个局部可变大小的数组,除了你在读取输入时增长它,而不是在你完成读取之前选择一个大小。)

Anyway, the normal way (pushing a pointer) looks like this:无论如何,正常的方式(推送一个指针)是这样的:

static const char str[] = "Hello World!";
void foo(const char*p);
int hello(void) { 
  foo(str);
  return 0;
}

and compiles to the following 32bit code (where the calling convention uses the stack)并编译为以下 32 位代码(其中调用约定使用堆栈)

hello:
        subl    $24, %esp       # keep the stack aligned for foo()
        pushl   $str            # push-immediate with the address of the string
        call    foo
        xorl    %eax, %eax      # return value
        addl    $28, %esp       # pop the arg and the padding
        ret

.section        .rodata
        .align 8
str:
        .string "Hello World!"

Note that I used const char str[] instead of const char *str so the symbol address would be the string-constant's address, rather than the address of a pointer.请注意,我使用了const char str[]而不是const char *str因此符号地址将是字符串常量的地址,而不是指针的地址。 Using const char *str results in a load (pushing contents of memory), instead of a push-immediate, to set up the args for foo.使用const char *str导致加载(推送内存内容),而不是立即推送,以设置 foo 的 args。 Using static const char *str lets the compiler resolve the indirection at compile time, and push $.LC0, %edi , never bothering to put a pointer to .LC0 in the .rodata section.使用static const char *str可以让编译器在编译时解析间接push $.LC0, %edipush $.LC0, %edi ,而.LC0在 .rodata 部分放置指向.LC0的指针。

In 64bit mode, compiling with -fPIC will force the compiler to use a RIP-relative lea instead of a mov-immediate.在 64 位模式下,使用-fPIC进行编译将强制编译器使用 RIP 相关的lea而不是 mov-immediate。 In 32bit code -fPIC , generate some really clunky stuff to get the current value of EIP and find the symbol address in the Global Offset Table.在 32 位代码-fPIC ,生成一些非常笨重的东西来获取 EIP 的当前值并在全局偏移表中找到符号地址。

I made hello return 0 instead of also being void, so I didn't have to explain tail-call optimization ( jmp instead of call ).我让hello返回 0 而不是 void,所以我不必解释尾调用优化( jmp而不是call )。 (Which doesn't happen in 32bit mode because the caller has to pop the arg after foo returns. When I first wrote this answer, I forgot that the question was about the stack, rather than just passing strings to functions. The 64bit ABI passes args in registers.) (这在 32 位模式下不会发生,因为调用者必须在 foo 返回后弹出 arg。当我第一次写这个答案时,我忘记了问题是关于堆栈的,而不仅仅是将字符串传递给函数。64 位 ABI 通过寄存器中的参数。)

This is from a tutorial I wrote, you just need to define your string and then mov the string to the stack.这是我写的一个教程,你只需要定义你的字符串,然后将字符串移动到堆栈中。 In reality it is moving only the start address, then when you want to print you specify the length for the offset.实际上,它只移动起始地址,然后当您要打印时指定偏移量的长度。

Compiling assembler: nasm -felf64 "helloworld.asm"...
Linking object helloworld.o: ld -melf_x86_64 -o helloworld helloworld.o ...
Displaying helloworld.asm asm source: 
section     .text
global      _start                              ;must be declared for linker (ld)

_start:                                         ;tell linker entry point

    mov     rdx,len                             ;message length
    mov     rcx,msg                             ;message to write
    mov     rbx,1                               ;file descriptor (stdout)
    mov     rax,4                               ;system call number (sys_write)
    int     0x80                                ;call kernel

    mov     rax,1                               ;system call number (sys_exit)
    int     0x80                                ;call kernel

section     .data

msg     db  'Hello, world!',0xa                 ;our dear string
len     equ $ - msg                             ;length of our dear string

Executing linked helloworld executable! ./helloworld | figlet ..
 _   _      _ _                             _     _ _ 
| | | | ___| | | ___    __      _____  _ __| | __| | |
| |_| |/ _ \ | |/ _ \   \ \ /\ / / _ \| '__| |/ _` | |
|  _  |  __/ | | (_) |   \ V  V / (_) | |  | | (_| |_|
|_| |_|\___|_|_|\___( )   \_/\_/ \___/|_|  |_|\__,_(_)
                    |/                                
./helloworld: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), statically linked, not stripped

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM