You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

64位NASM+GCC编译报错R_X86_64_32S:共享对象无法使用该重定位

Let's work through your problems one by one, starting with the linking error you hit first.

The Initial R_X86_64_32S Linking Error

That error happens because modern GCC defaults to building Position-Independent Executables (PIEs) for security reasons. Your original code uses push hi, which tries to use a 32-bit absolute address reference to your string in the .data section. PIEs can't use this kind of direct 32-bit addressing—they need code that can run at any memory address, so absolute 32-bit offsets aren't allowed (they limit where the program can load, and pose security risks).

There are two easy fixes for this:

  1. Disable PIE during linking: Add -no-pie to your GCC command to tell the linker to build a traditional non-position-independent executable:
    nasm -f elf64 main.asm
    gcc main.o -o main -no-pie
    rm main.o
    
    This lets your original push hi/call puts code work (though it's not following x86-64 calling conventions, which we'll cover next).
  2. Use x86-64's standard calling convention: The System V AMD64 calling convention (used on Linux) passes the first argument in the rdi register, not on the stack. This is the cleaner, modern approach that works with PIEs by default.

Why Your Modified Code Caused a Segmentation Fault

Your updated code tried to manually access the GOT (Global Offset Table) with call [puts wrt ..got], but that's unnecessary—and wrong. When you call a dynamic library function like puts, the linker automatically creates a PLT (Procedure Linkage Table) stub that handles lazy loading of the function through the GOT. Directly dereferencing the GOT entry before the dynamic linker has populated it will cause a segfault, since that memory isn't valid yet.

Instead, just use call puts directly—the linker takes care of the PLT/GOT magic for you.

Understanding rbp and rax in x86-64 Code

Let's break down what those registers are doing:

  • rbp (Base Pointer): This is used as a frame pointer to mark the start of your function's stack frame. The sequence push rbp; mov rbp, rsp sets up the frame pointer:
    • push rbp saves the previous frame pointer on the stack so the caller's stack frame isn't lost.
    • mov rbp, rsp sets the current stack pointer as the base for accessing local variables and function arguments.
      This is optional for simple functions (you can omit it to save instructions), but it makes debugging much easier—debuggers use frame pointers to unwind the call stack. Your modified code pushed rbp but didn't set mov rbp, rsp, which is an incomplete frame setup (though it wasn't the cause of your segfault).
  • rax (Accumulator Register): In x86-64 System V:
    • It holds the return value of a function (for integer/pointer types). For main, returning 0 tells the OS the program exited successfully—hence mov rax, 0 before ret.
    • For variadic functions (like printf), you set rax to the number of floating-point arguments passed in XMM registers. Since puts isn't variadic, you don't strictly need to set rax before calling puts, but setting it to 0 for main's return is required.

The Correct Working Code

Here's a properly written version that follows x86-64 conventions, works with PIEs, and avoids errors:

global main
extern puts

section .data
hi db 'hello', 0

section .text
main:
    ; Optional: Set up stack frame (good for debugging)
    push rbp
    mov rbp, rsp

    ; Pass the first argument to puts in rdi (x86-64 System V convention)
    mov rdi, hi
    call puts       ; Linker handles PLT/GOT automatically

    ; Optional: Tear down stack frame
    pop rbp

    ; Return 0 (success) from main
    mov rax, 0
    ret

Compile this with your original commands—no -no-pie needed, since it uses position-independent-friendly register addressing.

内容的提问来源于stack exchange,提问作者Ethan McTague

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:51:01