Asm Innsrion в обнаженной функции - PullRequest
0 голосов
/ 29 ноября 2018

У меня есть Ubuntu 16.04, x86_64 arch, 4.15.0-39-generic версия ядра.GCC 8.1.0

Я пытался переписать эту функцию (с первого поста https://groups.google.com/forum/#!topic/comp.lang.c++.moderated/qHDCU73cEFc) с диалекта Intel на AT & T. И у меня не получилось.

namespace atomic {
  __declspec(naked)
  static void*
  ldptr_acq(void* volatile*) {
    _asm {
      MOV EAX, [ESP + 4]
      MOV EAX, [EAX]
      RET
    }
  }

  __declspec(naked)
  static void*
  stptr_rel(void* volatile*, void* const) {
    _asm {
      MOV ECX, [ESP + 4]
      MOV EAX, [ESP + 8]
      MOV [ECX], EAX
      RET
    }
  }
}

Тогда я написалпростая программа, чтобы получить тот же указатель, который я передаю внутрь. Я установил GCC версии 8.1 с поддерживаемыми голыми атрибутами (https://gcc.gnu.org/gcc-8/changes.html «Порт x86 теперь поддерживает атрибут голой функции») для функций.помните, этот атрибут говорит компилятору не создавать пролог и эпилог функции, и я могу сам взять параметры из стека и вернуть их. Код: (не работает с segfault)

#include <cstdio>
#include <cstdlib>

  __attribute__ ((naked))
  int *get_num(int*) {
    __asm__  (
      "movl 4(%esp), %eax\n\t"
      "movl (%eax), %eax\n\t"
      "ret"
    );
  }

int main() {
    int *i =(int*) malloc(sizeof(int));
    *i = 5;

    int *j = get_num(i);
    printf("%d\n", *j);

    free(i);
    return 0;
}

затем я попытался использовать 64-битные регистры: (не работает с segfault)

__asm__  (
  "movq 4(%rsp), %rax\n\t"
  "movq (%rax), %rax\n\t"
  "ret"
);

И только после того, как я вынул значение из регистра rdi - все заработало.

__asm__  (
  "movq %rdi, %rax\n\t"
  "ret"
);

Почему я не смог выполнить передачу через регистр стека? Возможно, я допустил ошибку. Скажите, пожалуйста, где мой сбой?

1 Ответ

0 голосов
/ 29 ноября 2018

Поскольку в соглашении о вызовах System V x86-64 аргументы передаются в регистрах, а не в стеке, в отличие от старого неэффективного соглашения о вызовах System v в i386.

Вы всегда должны писать asm, соответствующее соглашению о вызовах,если вы пишете всю функцию в asm, например, с помощью функции naked или резервного файла .S.

расширенный asm GNU C позволяет вам использовать операнды для указания входных данных для asmзаявление, и компилятор будет генерировать инструкции, чтобы это произошло.(Я бы не рекомендовал использовать его, пока вы не поймете asm и как компиляторы превращают C в asm с включенной оптимизацией.)


Также обратите внимание, что movq %rdi, %rax реализует long *foo(long*p){return p;}, а не return *p.Возможно, вы имели в виду mov (%rdi), %rax для разыменования аргумента arg?


И кстати, вам определенно не нужен и не следует использовать встроенный asm для этого. https://gcc.gnu.org/wiki/DontUseInlineAsm, и см. https://stackoverflow.com/tags/inline-assembly/info

В GNU C вы можете привести указатель к volatile uint64_t*.Или вы можете использовать __atomic_load_n (ptr, __ATOMIC_ACQUIRE), чтобы получить в основном все, что вы получали от этого ассемблера, без дополнительных затрат на вызов функции или каких-либо затрат для оптимизатора на сайте вызова, когда все регистры с вызовом-заглухом были перекрыты.

Вы можете использовать их на любом объекте: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html В отличие от C ++ 11, где вы можете выполнять только атомарные операции на std::atomic<T>.

...