Ассемблер (в Linux)
Модератор: Olej
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
Только:
а). применительно к ассемблерным врезкам в коде (модулей) ядра Linux (модули ядра (римэйк) + Книга: "Расширения ядра Linux: драйверы и модули")
б). и только в виде инлайновых врезок GCC в C код.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
Но, может кого-то заинтересует ассемблер глубже ... а ещё поэтому - я буду выкладывать здесь ссылки на источники на ассемблерные описания.
Ассемблер вообще:
Reverse Engineering для начинающих
Шпаргалка по основным инструкциям ассемблера x86/x64
Полная документация GNU binutils: Documentation for binutils 2.40 ... В составе ассемблер as (AT&T): Using as.
This file is a user guide to the GNU assembler as (GNU Binutils) version 2.40.
Код: Выделить всё
olej@R420:~$ as --version
GNU ассемблер (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
Эта программа является открытым программным обеспечением; вы можете
распространять её согласно условиям GNU General Public License версии 3 или
более новой версии.
Эта программа не имеет абсолютно никаких гарантий.
Ассемблер настроен на цель x86_64-linux-gnu.
6.47 How to Use Inline Assembly Language in C Code
6.47.1 Basic Asm — Assembler Instructions Without Operands
6.47.2 Extended Asm - Assembler Instructions with C Expression Operands
How to Convert Basic asm to Extended asm
Ассемблерные вставки в компиляторе gcc
Ассемблерные вставки в GCCМГУ ©2013
GCC Inline ASM
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
По синтаксису записи ... от самого элементарного - сам синтаксис записи:
Код: Выделить всё
int main(int argc, char *argv[]) {
asm volatile(
"nop\n"
"nop\n"
"nop\n"
);
return 0;
}
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ make nop
cc -Wall -Wno-unused-result -Wno-format -O3 -O3 nop.c -o nop
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ls -l nop*
-rwxrwxr-x 1 olej olej 15776 фев 9 20:01 nop
-rw-r--r-- 1 olej olej 122 фев 7 18:19 nop.c
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ldd nop
linux-vdso.so.1 (0x00007ffcb45d8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fead7c4c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fead7ea0000)
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ./nop
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$
- Вложения
-
- nop.c
- (122 байт) 26 скачиваний
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
Важно помнить, что после компиляции Си-программы, код ассемблерной вставки
будет транслироваться gas, а не nasm. Естественным синтаксисом gas является AT&T, существенно
отличающийся от синтаксиса Intel. Современные версии gas поддерживают синтаксис Intel,
переключиться на него можно специальной директивой, но диалект поддерживаемого синтаксиса
будет отличаться от диалекта nasm рядом особенностей.
Синтаксис оператора ассемблерной вставки следующий.
__asm__ (вставка : список_выходных_операндов : список_входных_операндов :
список_разрушаемых_регистров );Код: Выделить всё
__asm__ ( ".intel_syntax noprefix\n\t" "mov eax, %1\n\t" "mov %0, eax\n\t" /* ассемблерная вставка */ : "=r"(b) /* выходные операнды */ : "r"(a) /* входные операнды */ : "%eax" /* разрушаемые регистры */ );
вставка представляет собой строковую константу с ассемблерными инструкциями. В теле вставки
могут находиться не только ассемблерные инструкции, но и вообще любые директивы,
распознаваемые ассемблером gas. В частности, это позволяет изменить используемый им
синтаксис инструкций по-умолчанию.
Пример 1Директива .intel_syntax меняет синтаксис AT&T на синтаксис Intel; необходимоКод: Выделить всё
__asm__ ( ".intel_syntax noprefix\n\t" "mov eax, %1\n\t" "mov %0, eax\n\t" /* ассемблерная вставка */ : "=r"(b) /* выходные операнды */ : "r"(a) /* входные операнды */ : "%eax" /* разрушаемые регистры */ );
дополнительно указывать смену синтаксиса для операндов инструкций, noprefix, что позволит
писать код в более близком к диалекту nasm виде, не используя при записи имен регистров
префикс %.
Для связи ассемблерных инструкций с переменными Си-программы используются следующие за
вставкой два элемента оператора: списки операндов, в которых операнды перечислены через
запятую. Каждый описанный операнд затем может использоваться в ассемблерных инструкциях,
обращение к нему осуществляется по номеру с префиксом %. Нумерация начинается с 0, и идет
непрерывно, объединяя все элементы списков выходных и входных операндов.
Для выходных операндов строка ограничения типа должна начинаться с символа `=`. Следующий
в строке символ указывает (кодирует), куда компилятору нужно поместить значение
соответствующей переменной. Способ кодировки одинаковый для входных и выходных
операндов вставки.
: "=r"(b) /* выходные операнды */
: "r"(a) /* входные операнды */
Для рассматриваемого примера описание операндов требует размещения значения Си-
переменной a на каком-либо регистре общего назначения (задано символом r), перед тем как
начнет выполняться код вставки. После того как код вставки завершится, значение переменной b
будет находиться на регистре общего назначения и его потребуется записать в переменную b.
Пример того, как компилятор о
Компилятору о побочном эффекте, возникающем из-за копирования, необходимо сообщить
ключевым словом __volatile__ после ключевого слова __asm__. Это обезопасит корректность
программы, поскольку в отсутствии этого ключевого слова некоторые оптимизирующие
преобразования потенциально могут перемещать код, в том числе и код ассемблерных вставок.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
Выполнив командуполучим файл test_asm.sКод: Выделить всё
gcc test_asm.cpp -S
...
При желании, можно получить несколько более подробный код (с дополнительными комментариями), воспользовавшись опцией -fverbose-asm.
Построчный листинг (в файле test_asm.lst) C++ и ассемблера можно получить, воспользовавшись командойКод: Выделить всё
gcc -c -g -Wa,-a,-ad test_asm.cpp > test_asm.lst
GCC вставляет, после возможных макроподстановок, текст ассемблерной вставки непосредственно в выходной файл на языке ассемблера.
...
Во-первых, компилятор почти не "подглядывает" в указанный программистом код ассемблерной вставки. Он при необходимости проводит макроподстановки (см. ниже), но в общем передаёт код ассемблеру почти "как есть", и почти не имеет представления о происходящем внутри. В частности, обнаружением ошибок занимается именно ассемблер (GCC передаёт программисту сообщения об ошибках от ассемблера).
Во-вторых, как следствие, ассемблерная вставка является для компилятора (и в частности оптимизатора) единой непрозрачной командой (чёрным ящиком). Всё нужную ему информацию о том, как этот блок взаимодействует с окружающим миром, компилятор получает напрямую от программиста, из явно указанных операндов ассемблерной вставки (задающих связи ассемблерного кода с переменными C++ и список задействованных ресурсов (регистров и т. д.) и изменений (состояния флагов, памяти и т. д.) в ассемблерной вставке), а не из детального рассмотрения текста ассемблерной вставки (которого он не производит). Технически говоря, GCC предоставляет программисту интерфейс к Register Transfer Language. Ответственность о соответствии действительности информации, указанной в операндах, целиком лежит на программисте.
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
Дальше...
То как производятся все системные вызовы Linux в 32-бит системе:
Код: Выделить всё
$ cat write.32.c
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <asm/unistd_32.h>
#ifdef __i386__
int write_call(int fd, const char* str, int len) {
long __res; // это ещё C определение
__asm__ volatile ("int $0x80"
: "=a" (__res)
: "0"(__NR_write),"b"((long)(fd)),"c"((long)(str)),"d"((long)(len)));
return (int) __res; // а это уже C оператор
}
#endif
int main(int argc, char *argv[]) {
#ifdef __i386__
char* string = "русская строка для вывода\n";
if (!write(1, string, strlen(string)))
printf("ошибка вывода\n");
return 0;
#else
char* string = "это написано только для 32-бит Linux!\n";
if(!write(1, string, strlen(string)))
printf("ошибка вывода\n");
return 1;
#endif
}
Код: Выделить всё
$ inxi -S
System: Host: lmde32 Kernel: 5.10.0-21-686 i686 bits: 32 Desktop: Cinnamon 5.6.7 Distro: LMDE 5 Elsie
$ uname -a
Linux lmde32 5.10.0-21-686 #1 SMP Debian 5.10.162-1 (2023-01-21) i686 GNU/Linux
Код: Выделить всё
$ make
cc -Wall -Wno-unused-result -O3 -O3 write.32.c -o write.32
Код: Выделить всё
$ ./write.32
русская строка для вывода
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ make write.32
cc -Wall -Wno-unused-result -Wno-format -O3 -O3 write.32.c -o write.32
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ./write.32
это написано только для 32-бит Linux!
- Вложения
-
- write.32.c
- (870 байт) 26 скачиваний
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
Чтение счётчика RDTSC числа циклов тактовой частоты процессора ... и оценка частоты работы процессора:
Код: Выделить всё
olej@esprimop420:~/2023/own.BOOKs/GASM.next.inWORK$ cat clock.c
#include <stdint.h>
#include <time.h>
#include <stdio.h>
uint64_t rdtsc(void) {
union sc {
struct {uint32_t lo, hi;} r;
uint64_t f;
} sc;
__asm__ __volatile__ ("rdtsc" : "=a"(sc.r.lo), "=d"(sc.r.hi));
return sc.f;
}
// вариант для старых GCC:
// __asm__ __volatile__ ( ".byte 0x0f, 0x31" : "=a"( sc.r.lo ), "=d"( sc.r.hi ) );
// вариант но только для 32-бит i686:
// asm volatile ( "rdtsc" : "=A" (x) );
#define NUMB 10
uint64_t calibr(int rep) {
uint64_t n, m, sum = 0;
n = m = (rep >= 0 ? NUMB : rep);
while(n--) {
uint64_t cf, cs;
cf = rdtsc();
cs = rdtsc();
sum += cs - cf;
}
return sum / m;
}
uint64_t proc_hz(void) {
time_t t1, t2;
int64_t cf = 0, cs = 0;
time(&t1);
while(t1 == time(&t2)) // начало след. секунды
cf = rdtsc();
while(t2 == time(&t1)) // начало след. секунды
cs = rdtsc();
return cs - cf - calibr(1000); // с учётом времени вызова rdtsc()
}
int main(int argc, char *argv[]) {
for (int i = 1; i < 5; i++)
printf("%016lX | ", rdtsc());
printf("\n");
for (int i = 10; i < 5000; i *= 10)
printf("%06X | ", calibr(i));
printf("\n");
printf("%llu\n", proc_hz());
return 0;
}
Код: Выделить всё
olej@esprimop420:~/2023/own.BOOKs/GASM.next.inWORK$ make clock
cc -Wall -Wno-unused-result -Wno-format -O3 -O3 clock.c -o clock
Код: Выделить всё
olej@esprimop420:~/2023/own.BOOKs/GASM.next.inWORK$ sudo chrt -r 50 taskset -c 0 ./clock
[sudo] пароль для olej:
000274A63DFA328E | 000274A63DFD95CC | 000274A63DFD9B3B | 000274A63DFD9D04 |
000016 | 000016 | 000016 |
3392144142
Код: Выделить всё
olej@esprimop420:~/2023/own.BOOKs/GASM.next.inWORK$ inxi -Cxxx
CPU: Info: Quad Core model: Intel Xeon E3-1240 v3 bits: 64 type: MT MCP arch: Haswell rev: 3
L2 cache: 8 MiB
flags: avx avx2 lm nx pae sse sse2 sse3 sse4_1 sse4_2 ssse3 vmx bogomips: 54276
Speed: 3592 MHz min/max: 800/3800 MHz Core speeds (MHz): 1: 3592 2: 3592 3: 3592 4: 3592
5: 3592 6: 3592 7: 3590 8: 3594
- Вложения
-
- clock.c
- (1.3 КБ) 24 скачивания
- Olej
- Писатель
- Сообщения: 21338
- Зарегистрирован: 24 сен 2011, 14:22
- Откуда: Харьков
- Контактная информация:
Ассемблер (в Linux)
Длина текстовой строки... Разминаемся...
Эквивалент вот этого:
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ cat strlen0.c
#include <stdio.h>
#include <string.h>
long lenstr(char* s) {
return strlen(s);
}
int main(int argc, char **argv) {
char* str = "test string";
if (argc > 1) str = argv[1];
printf("строка: <%s> длина %ld байт\n", str, lenstr(str));
}
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ make strlen0
cc -Wall -Wno-unused-result -Wno-format -O3 -O3 strlen0.c -o strlen0
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ./strlen0
строка: <test string> длина 11 байт
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ./strlen0 'русская строка'
строка: <русская строка> длина 27 байт
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ cat strlen1.c
#include <stdio.h>
#include <stdint.h>
long lenstr(char* s) {
long res = 0; // это ещё C определение
__asm__ volatile (
"0:\n\t" //метка
"incq %0\n\t"
"cmpb $0,(%1,%0)\n\t"
"jne 0b\n\t" //переход назад (b)
: "=rax"(res), "=D"(s)
: "0"(res), "D"(s)
: "cc"
);
return res;
}
int main(int argc, char **argv) {
char* str = "test string";
if (argc > 1) str = argv[1];
printf("строка: <%s> длина %ld байт\n", str, lenstr(str));
}
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ make strlen1
cc -Wall -Wno-unused-result -Wno-format -O3 -O3 strlen1.c -o strlen1
Код: Выделить всё
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ./strlen1
строка: <test string> длина 11 байт
olej@R420:~/2023/own.BOOKs/BHV.kernel.new/GASM.next.inWORK$ ./strlen1 'русская строка'
строка: <русская строка> длина 27 байт
Кто сейчас на конференции
Сейчас этот форум просматривают: FAST WebCrawler [Crawler] и 6 гостей