#assembly #gcc #inline-assembly #arm64
#сборка #gcc #встроенная сборка #arm64
Вопрос:
Это код, который я использовал для записи некоторых данных в память для отладки (пока printf не будет доступен в программе u-boot). Переменная myptr
находится в .__mydebug
разделе и увеличивается на 8 после каждой 8-байтовой записи, и я хочу записать любое значение, которое меня интересует, в виде {debug_tag, debug_value}
пары. Вот debug_tag
некоторое значение для отображения последовательности данных отладки, и это debug_value
значение, которое я хочу проверить (или увидеть) во время отладки. Это сборка arm64.
.global myptr
ldr x28, =myptr /* load the address of myptr */
add x28, x28, #8 /* set write pointer to the next address after the myptr variable */
mov x27, #0x33 /* first debug write starts with tag value 0x33 */
str x27, [x28], #8 /* write the tag value, increment the pointer */
mov x27, some_value /* some_value : the value I want to see with tag value 0x33 */
str x27, [x28], #8 /* write the debug value, increment the pointer */ ldr x26, =myptr /* load pointer address */
/* next debug write, in the same assembly code, x28 hasn't changed, so use as is */
mov x27, #0x34 /* new debug tag */
str x27, [x28], #8 /* write new tag, increment pointer */
mov x27, some_another_value /* another data I want to check */
str x27, [x28], #8 /* write the data, increment pointer */
ldr x26, =myptr /* load the address of myptr to x26 */
str x28, [x26] /* save the updated pointer in myptr, just in case x28 is modified and
the pointer should be used later in assembly or C code .. */
.... (skip) ....
.section .__mydebug
myptr: .double 0x0
data_start: .double 0x0
Таким образом, это последовательная запись отладочной информации в раздел памяти, который работает.
Я могу продолжить эту отладочную запись позже в программе .c, как показано ниже, и это также работает.
// debug print
int xx=sizeof(struct global_data);
*((uint64_t *)myptr) = 0x101; myptr =8; /* debug tag start with 0x101 here */
*((uint64_t *)myptr) = xx; myptr =8; /* write some data I want to check.. */
*((uint64_t *)myptr) = 0x102; myptr =8; /* another debug tag */
*((uint64_t *)myptr) = base; myptr =8; /* another value I want to check */
Хорошо, я могу с этим смириться. Но это выглядит неприятно и неудобно.
Итак, мне любопытно, как я могу сделать это в программе C, используя функцию со встроенной сборкой. Я хочу передать функции значение тега и значение отладки (64-разрядное) в качестве аргументов. Функция должна извлекать myptr
значение для записи тега и данных и обновлять myptr
значение каждый раз. Я попытался написать функцию ниже.
void dbg_print(unsigned int tag, uint64_t data)
{
uint64_t ptr_addr1;
__asm (
"ldr %[ptr_addr], =myptr"
"ldr %[ptr_val], [%[ptr_addr]]"
"str %[tag_val], [%[ptr_val]], #8"
"str %[data_val], [%[ptr_val]], #8"
: /* no output */
: [tag_val] "r" (tag), [data_val] "r" (data) /* input list */
: "memory" /* no specific clobbered register, but memory modified */
);
}
Когда я его компилирую, я получаю эту ошибку компиляции.
common/init/board_init.c: In function 'dbg_print':
common/init/board_init.c:144:1: error: undefined named operand 'ptr_addr'
144 | );
| ^
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_addr'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
common/init/board_init.c:144:1: error: undefined named operand 'ptr_val'
make[2]: *** [scripts/Makefile.build:254: spl/common/init/board_init.o] Error 1
make[1]: *** [scripts/Makefile.spl:515: spl/common/init] Error 2
Я не могу понять undefined named operand
ошибку. Нужно ли мне где-то определять операнд в шаблоне? В примере в https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_qbn1517569205870.htm , операнды в шаблоне просто используются без определения. Переменные в C объявляются в любом случае, но разве операнды в шаблоне сборки не заменяются компилятором в любом случае?
Спасибо за чтение, и я был бы признателен, если бы кто-нибудь мог разъяснить мне эту вещь.
Добавить :
Прочитав комментарии Нейта Элдриджа и Питера Кордеса, я понял, что «определение операнда» означает подключение значения к миру C с использованием спецификаторов операндов (эти поля после : и связаны с :). Итак, я попытался изменить код на это, и я посмотрю, работает ли это.
void dbg_print(unsigned int tag, uint64_t data)
{
uint64_t ptra, ptrv;
__asm (
"ldr %[ptr_addr], =myptr n" /* get pointer address */
"ldr %[ptr_val], [%[ptr_addr]] n" /* get pointer value */
"str %[tag_val], [%[ptr_val]], #8 n" /* write to pointed addr */
"str %[data_val], [%[ptr_val]], #8 n" /* write to pointed addr */
: [ptr_addr] "=r" (ptra), [ptr_val] "=r" (ptrv)
: [tag_val] "r" (tag), [data_val] "r" (data) /* input list */
: "memory" /* no specific clobbered register, but memory modified */
);
}
Комментарии:
1. Я не уверен, почему вы хотите использовать встроенную сборку для простых загрузок и хранилищ, с которыми компилятор может справиться отлично. Вы могли бы записать его более чисто
volatile uint64_t *p = (volatile uint64_t *)myptr; p[0] = 0x101; p[1] = xx; p[2] = 0x102; p[3] = base;
или обернуть в красивую функцию или макрос.2. В примере операнды в шаблоне НЕ просто используются без определения; имена
lhs
иrhs
в шаблоне сопоставляются с именами, указанными для операндов в конце. В вашем коде не определен ни один вызываемый операндptr_addr
, поэтому он не работает. Он не будет автоматически сопоставляться с программной переменной с таким именем, если это то, что вам интересно.3. На самом деле должно быть
volatile uint64_t *p = (volatile uint64_t *)amp;myptr;
, если я правильно понимаю код.4. Указанные вами именованные операнды — это
[data_val]
and[tag_val]
, а не[ptr_addr]
and[ptr_val]
, поэтому сообщение об ошибке кажется довольно очевидным; GCC не знает, как развернуть%[ptr_addr]
, потому что вы не дали ни одному из операндов это имя в расширенной инструкции Asm. Как говорит Нейт, лучшим решением, вероятно, является полная замена инструкции inline-asm. Тем более, что у вас есть ошибки, такие как запись регистра без указания каких-либо"=r"
выходных операндов, только"r"
входные данные (что подразумевает доступ только для чтения).5. Привет, Нейт и Питер, спасибо за комментарии. Я получал от вас помощь много раз раньше ( :)) . Я обновил свой вопрос и попробую, если это сработает. кстати, я просто хотел использовать функцию, использующую встроенную сборку для этого случая. (для более четкого понимания).
Ответ №1:
Я не могу понять ошибку неопределенного именованного операнда. Нужно ли мне где-то определять операнд в шаблоне? В примере в https://www.keil.com/support/man/docs/armclang_ref/armclang_ref_qbn1517569205870.htm , операнды в шаблоне просто используются без определения.
Да, вам нужно определить их либо во входных, либо в выходных данных инструкции asm. Все примеры, которые вы связываете, определяют все имена, которые они используют. Ваш код просто определяет tag_val
и data_val
там, поэтому ptr_val
и ptr_addr
не определены.
Комментарии:
1. Я узнал об этом из комментария Питера Кордеса, но поскольку это был оригинальный вопрос, я выбираю его в качестве ответа. Спасибо!