1

I am trying to get the address of a label using inline asm in Zig.

const main_ccb_p1 = core.mainCCCBPtr();
comptime var to_return_label = ".to_return" ++ mangle(function);
asm volatile (
    \\ # save main context
    \\ movq %rbx,  0(%[ctx])    # save rbx
    \\ movq %r12,  8(%[ctx])    # save r12
    \\ movq %r13, 16(%[ctx])    # save r13
    \\ movq %r15, 24(%[ctx])    # save r15
    \\ movq %rsp, 32(%[ctx])    # save rsp
    \\ movq %rbp, 40(%[ctx])    # save rbp
    \\ 
    ++ "movq $" ++ to_return_label ++ ", 48(%[ctx])"
    :
    : [ctx] "{rax}" (&main_ccb_p1.context),
);

// some code
// ...

asm volatile (to_return_label ++ ":" ::: "memory");

It works fine on Linux. But I got some weird result on Windows. The address I expected is 0000 7FFF F661 2D60, but I got FFFF FFFF F661 2D60. Then I try to use lea label, %rcx. And I still got a wrong result 0000 0000 F661 2D60.

Why this happened? How to get the address of a label using inline assembly on Windows? Thanks for your help.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    The normal AT&T syntax for putting a symbol address into a register is `lea label(%rip), %rcx`. ([How to load address of function or label into register](https://stackoverflow.com/q/57212012)). `lea label, %rcx` uses a sign-extended disp32 absolute addressing mode. (Which can't produce a value like `0000 0000 F661 2D60` where the high bit of the low half is set, but the high half is not FFFFFFFF. You could get that with `mov $label, %ecx`, if Windows truncated the relocation instead of erroring like a Linux PIE would!). – Peter Cordes Apr 01 '23 at 05:21
  • I don't know Zig, but it's inline-asm syntax looks like it's based on GNU C. So you'd want a `"memory"` clobber in the statement with the stores, see [How can I indicate that the memory \*pointed\* to by an inline ASM argument may be used?](https://stackoverflow.com/q/56432259) . – Peter Cordes Apr 01 '23 at 05:24
  • Also, you normally need to write context-switch functions in stand-alone asm, not inline, since you need it not to inline into other functions, and to control how stack space is used, and to only have to save the call-preserved registers in the calling convention, not all of them. Or at least declare clobbers on them all; don't forget ZMM16..31 if AVX-512 is enabled, and k0..k7 – Peter Cordes Apr 01 '23 at 05:25
  • I wouldn't suggest using `lea` inside the inline asm; if the compiler isn't giving you a valid pointer, that's probably a bug (in the compiler, or UB in your code.) – Peter Cordes Apr 01 '23 at 06:03
  • @PeterCordes Your suggestions are very helpful! The reason why I use ininle asm is I need to cooperate with zig type system to call and pass the params to the coroutine functions. Thank you again! – 朕与将军解战袍 Apr 01 '23 at 07:25
  • If I had to guess, perhaps Zig is compiling `[ctx] "{rax}" (&main_ccb_p1.context)` to something like `mov $sign_extended_imm32, %rax` in a LargeAddressAware .exe. Which would be very silly, no code-size savings vs. RIP-relative LEA. If would work on Linux only in a non-PIE executable. Anyway, perhaps on Windows, there's a 32-bit runtime fixup that gets filled with the low 32 bits of the address into the instruction which sign-extends that to 64-bit, giving the high FFFs. If so that's a compiler bug (should be using RIP-relative LEA), and you can work around it in asm with your own LEA. – Peter Cordes Apr 01 '23 at 07:52
  • @PeterCordes What you mentioned is a little beyond my knowledge. At first, I just wanted to write a simple coroutine library for learning.I never expected that I would have to struggle with thing like `calling convention` and `caller/callee-saved registers`. Even now I am not 100% sure what registers I have to save on Linux and Windows. – 朕与将军解战袍 Apr 01 '23 at 09:19
  • [Why does Windows64 use a different calling convention from all other OSes on x86-64?](https://stackoverflow.com/q/4429398) / https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions But calling conventions only apply to whole functions, not inline-asm, unless you call functions from asm [Calling printf in extended inline ASM](https://stackoverflow.com/q/37502841). – Peter Cordes Apr 01 '23 at 17:11
  • Inline asm has to describe itself to the compiler via the clobber list. At least when you're using it in a supported manner, which for GNU C doesn't allow changing the stack pointer. (https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html). – Peter Cordes Apr 01 '23 at 17:13

0 Answers0