Страницы

Поиск по вопросам

Показаны сообщения с ярлыком x86-64. Показать все сообщения
Показаны сообщения с ярлыком x86-64. Показать все сообщения

вторник, 28 января 2020 г.

Как указать компилятору заполнить статический массив одинаковыми ненулевыми значениями?

#cpp #gcc #x86_64


Елементарный способ: произвести заполнение массива при инициализации. Но массив статический
и уже занимает место в образе. Нужно каким-то образом указать компилятору заполнить
область массива конкретным значением. 
Код static int arr[5]={1} заполняется как {1, 0, 0, 0, 0}, а нужно {1, 1, 1, 1, 1}

__attribute__((fillupper(1))) игнорируется gcc.

Массив неконстантный. Массив с 5 элементами - пример. Нужно заполнить массив в 4096
элементов.
Использование возможностей STL сильно ограничены, включая производные. Они все имеют
элементарное решение, указанное в первом предложении.

Доработка ответа:

template 
struct IArray {
  type val = v;
  IArray& operator=(type n){
    val=n;
    return *this;
  }
  IArray& operator|=(type n){
    val|=n;
    return *this;
  }
};


И необходимо дописать/изменить операторы для нужных Вам преобразований.
    


Ответы

Ответ 1



#include template struct Int { int val = v; }; int main() { Int<10> arr[1024]; std::cout << arr[10].val << std::endl; } https://godbolt.org/z/6-BEPi

Ответ 2



Не бейте меня. Нашёл только asm способ. //> g++ -std=c++11 memset.cpp #include asm( " .globl s2\n" " .data\n" " .align 32\n" " .type s2, @object\n" " .size s2, 16384\n" "s2: .fill 4096,4,777\n" ); extern "C" { extern int s2[4096]; }; int main(){ for(size_t j = 4096+1;j>0;){--j; std::cout<<"s2["<

Ответ 4



dd if=/dev/zero of=/tmp/sarr conv=swab count=4096 xxd -i /tmp/sarr | sed -e 's/0x00/0x01/g' > /tmp/parr.h P.S. :) UPD: #include template class ct_array { public: constexpr explicit ct_array(); public: const T& operator[](size_t idx) const; protected: T m_data[N]; }; template constexpr ct_array::ct_array() : m_data() { for (T *el = m_data; el != m_data + N; ++el) *el = def_val; } template const T& ct_array::operator[](size_t idx) const { return m_data[idx]; } template constexpr decltype(auto) make_ct_array() { return ct_array(); } int main() { constexpr auto arr = make_ct_array(); std::cout << arr[10] << std::endl; return 0; } UPD2: #include template class ct_array { public: constexpr explicit ct_array(T def); public: const T& operator[](size_t idx) const; protected: T m_data[N]; }; template constexpr ct_array::ct_array(T def) : m_data() { for (T *el = m_data; el != m_data + N; ++el) *el = def; } template const T& ct_array::operator[](size_t idx) const { return m_data[idx]; } template constexpr decltype(auto) make_ct_array(T def) { return ct_array(def); } int main() { constexpr auto arr = make_ct_array(3.14); std::cout << arr[10] << std::endl; return 0; }

Ответ 5



Можно написать функцию template void f(T a[], size_t s, const T& n) { for (size_t i = 0; i < s; ++i) a[i] = n; } Тогда const int n = 4096; int arr[n]; f(arr, n, 1); после ее вызова вы получите желаемый результат Или же воспользоваться стандартным альгоритмом const int n = 4096; int arr[n]; std::fill(arr, arr + n, 1);

среда, 22 января 2020 г.

В разница между командами hlt и pause? Что лучше использовать?

#ассемблер #x86_64


hlt "останавливает" процессор до прихода прерывания, что снижает нагрузку. pause
тоже "останавливает", но не могу найти конкретного описания. Коды у них разные. 
    


Ответы

Ответ 1



Ух. Попробую ответить. Основная идея, как я понял, в том, что HLT (halt) - останавливает логический процессор, освобождая доступ к общим ресурсам в физическом пакете, которыми могут воспользоваться все активные логические процессоры в этом же пакете. А PAUSE используется для того чтобы повысить производительность в процессорах с технологией Hyper-Threading. Суть в том что код, который ожидает освобождения чего-либо путем обращения к семафору, находится в wait цикле. Если код выйдет из цикла и обнаружит, что порядок в памяти нарушен (memory order violation), процессор просто сбросит конвейер (pipeline flush). Это приведет к проседанию производительности в целом. PAUSE таким образом нужен для того, чтобы подсказать процессору что кодовая последовательность является wait циклом, и помогает избежать memory order violation и pipeline flush. Также эта инструкция дает какие-то бонусы в энергопотреблении, но я не вчитывался. Можете почитать еще тут: https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce-302.html

среда, 25 декабря 2019 г.

Права доступа к страницам в linux x86_64

#linux #memory #x64 #x86_64 #виртуальная_память


Изучаю как устроена атака на переполнение буфера. Написал вот такую программу

typedef void(*Function)();
int main() {
    const char* shell = "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90";
    Function f = (Function)shell;
    f();
    return 0;
}


shell - пара десятков nop-ов.(0x90 - опкод nop-a).
Пошагово прошелся по ней в gdb и она падает от segfault при вызове f, а точнее при
выполнении инструкции

callq    *%rdx


При этом в rdx записан адрес 0x4005d8( Именно по этому адресу располагаются nop-ы).
И все это можно было бы объяснить тем, что адрес 0x4005d8 относится к странице у которой
не выставлено право на исполнение, но:

1) Вот вывод утилиты pmap для этого процесса

26040:   ./main
0000000000400000      4K r-x-- main
0000000000600000      4K r---- main
0000000000601000      4K rw--- main
0000000001c65000    132K rw---   [ anon ]
00007f40de3ce000   1792K r-x-- libc-2.23.so
00007f40de58e000   2048K ----- libc-2.23.so
00007f40de78e000     16K r---- libc-2.23.so
00007f40de792000      8K rw--- libc-2.23.so
00007f40de794000     16K rw---   [ anon ]
00007f40de798000    152K r-x-- ld-2.23.so
00007f40de995000     12K rw---   [ anon ]
00007f40de9bd000      4K r---- ld-2.23.so
00007f40de9be000      4K rw--- ld-2.23.so
00007f40de9bf000      4K rw---   [ anon ]
00007ffd8e874000    132K rw---   [ stack ]
00007ffd8e924000     12K r----   [ anon ]
00007ffd8e927000      8K r-x--   [ anon ]
ffffffffff600000      4K r-x--   [ anon ]
 total             4356K


И если я правильно понимаю что тут написано, то у диапазона адресов 0х400000-0х60000
есть право на исполнение.

2) Что подтверждает предыдущий пункт: адрес функции main был 0х400507 что довольно
близко к последовательности nop-ов.

Однако если в rdx положить адрес main то никакого segfault-а не происходит. Проясните,
пожалуйста, что не так.
    


Ответы

Ответ 1



Учитывая, что сегмент константных данных (а именно туда попадает const char* shell) отображается без права на исполнение, скорее всего, вы столкнулись с ASLR. ASLR (Address Space Layout Randomisation, разбиение адресного пространства случайным образом) — это механизм, который как раз и направлен против шелл-атак, полагающихся на фиксированный адрес тех или иных участков памяти. Он разбрасывает программу, библиотеки и стек случайным образом по всей виртуальной памяти, доступной программе, причём из запуска в запуск результат подобного распределения отличается. При первом запуске (под gdb) адрес 0x4005d8 попадал в сегмент неизменяемых данных программы (на это указывают права доступа сегмента, да и const-переменные находятся только там). При втором же запуске (для pmap) по тому адресу стал располагаться сегмент кода. Если вы хотите получить согласованный результат, запускайте pmap параллельно с gdb, в другом окне или вкладке консоли. (Немного о праве на исполнение блока памяти) Начиная с версии 2.3.23, Linux имеет встроенную поддержку DEP. DEP (Data Execution Prevention, предотвращение выполнения данных) — это механизм, направленный на запрет исполнения кода там, где должны лежать данные (к примеру, в стеке или сегменте данных). Он работает за счёт того, что к уже существующим флагам прав доступа к странице (чтение, запись, доступ только из режима ядра и т. д.) был добавлен ещё один (запрет на исполнение), который операционная система устанавливает для всех страниц, не соответствующих сегментам кода исполняемых файлов (а main() как раз и лежит в подобном сегменте). Единственное, где может сработать ваш шелл — это в областях памяти, созданных при помощи mmap() с одновременно выставленными флагами PROT_EXEC и PROT_WRITE.

суббота, 30 ноября 2019 г.

Как использовать данные о размерах кэшей процессора для ускорения программы?

#cpp #ассемблер #x64 #x86 #x86_64


У каждого процессора есть кэш различных уровней и размеров. 


Есть ли смысл писать программу таким образом, что б абсолютно все используемые массивы,
переменные и т.п. в оперативной памяти занимали 
строго последовательные адреса? Т.е. например: начальный адрес 90870000, конечный
90880000. Есть смысл что б этом промежутке были исключительно данные конкретной программы?
Как влияет на скорость программы соотношение размера кэшей и используемого размера
оперативной памяти?
Есть ли смысл обработку данных во много раз больших размера кэшей проводить блоками?
Каждый блок меньше размера кэша.
В ассемблере есть инструкции по записи в оперативную память без использования кэша.
Какой в этом смысл?
Какую из двух инструкций и в каких случаях использовать для возврата 128 бит результата
обработки обратно в память?

     movdqu [ebx],xmm0
     movntps [ebx],xmm0//без использования кэша



Правильно я понимаю, что если эти 128 бит больше не нужны для обратотки, то лучше
movntps? Это быстрее?


Предположим есть чернобелое bmp изображение 1024*1024.
Перенесем все его пиксели в:

    unsigned __int8 *src_img
    src_img = new unsigned __int8[1024*1024];//каждый байт это значение одного пикселя
от 0 до 255



Допустим есть два варианта алгоритма:
1) копируем в xmm0 128 бит из src_img, что-то делаем в xmm регистрах и возвращаем
измененные 128бит обратно по тому же адресу в оперативную память. Самое важное эти
128 бит выбираются от начала src_img последовательно до конца.
2) Делаем тоже самое, но 128 бит выбираются не последовательно,а из разных мест src_img
1-й вариант будет быстрее или нет? Или формулируя по другому: первоначально программа
берет данные из: movdqu xmm0,[ebx]. Имеет различие для быстродействия насколько далеко
от первоначального адреса программа берет следующие 128 бит? 

Изучаю данные вопросы в контексте вот этой задачи:
https://stackoverflow.com/questions/50747393/prewitt-edge-detection-algorithm-using-x86-mmx-simd
    


Ответы

Ответ 1



Есть смысл что б этом промежутке были исключительно данные конкретной программы? Это зависит от программы. Если она оч. часто обращается к этим переменным, то да, конечно - размещение всех ее данных в блоке, который занимает минимальное к-во кеш-линий, ускорит эти операции. Обычно не говорят о размещении всех данных в одной кеш-линии. На практике это невозможно. Обычно говорят о "кратном рамере" блока данных. Как влияет на скорость программы соотношение размера кэшей и используемого размера оперативной памяти? Особой разницы с первым вопросом не вижу. Если у Вас программа оптимизирована под загрузку кеша, и учитывает его размер, конечно, она будет быстрее работать на железе с большим кешем. Надеюсь, Вы так же вкурсе, что кеши есть разных уровней (level1, level2, level3, кеш данных, кеш инструкций), их размеры могут отличаться, и скорость доступа к ним, естественно, тоже разная, т.е. поле для оптимизаций - обширное настолько, насколько Вам хватит терпения перебирать различное железо для экспериментов и фантазии на реализацию алгоритмов. Есть ли смысл обработку данных во много раз больших размера кэшей проводить блоками? Каждый блок меньше размера кэша. Нет. Есть смысл распараллелить эту обработку между ядрами таким образом, чтобы у каждого ядра эти порции данных, по возможности, не пересекались. Вот тут кратность размера обрабатываемого блока данных имеет большое значение. Порции следует разделить так, чтобы кеш-линии одного ядра не пересекались с кеш-линиями другого. В ассемблере есть инструкции по записи в оперативную память без использования кэша. Какой в этом смысл? Смысл в том, чтобы не делать лишнюю работу: если Вам нужно только переместить блок данных, и Вы не обращаетесь к нему для каких-то рассчетов, то и в кеш его нет необходимости загружать. Имеет различие для быстродействия насколько далеко от первоначального адреса программа берет следующие 128 бит? Нет, не должно. На 64битных кеш-линиях, по крайней мере, последовательное чтение 128битных значений в непараллельной программе выгоды не дает. В этом случае определяющим будет размер кеша и параллелизм между ядрами (кооперация кэшей). Ждите или ищите 256битных кеш линий, по крайней мере :)

пятница, 29 марта 2019 г.

Как указать компилятору заполнить статический массив одинаковыми ненулевыми значениями?

Елементарный способ: произвести заполнение массива при инициализации. Но массив статический и уже занимает место в образе. Нужно каким-то образом указать компилятору заполнить область массива конкретным значением. Код static int arr[5]={1} заполняется как {1, 0, 0, 0, 0}, а нужно {1, 1, 1, 1, 1}
__attribute__((fillupper(1))) игнорируется gcc.
Массив неконстантный. Массив с 5 элементами - пример. Нужно заполнить массив в 4096 элементов. Использование возможностей STL сильно ограничены, включая производные. Они все имеют элементарное решение, указанное в первом предложении.
Доработка ответа:
template struct IArray { type val = v; IArray& operator=(type n){ val=n; return *this; } IArray& operator|=(type n){ val|=n; return *this; } };
И необходимо дописать/изменить операторы для нужных Вам преобразований.


Ответ

#include
template struct Int { int val = v; };
int main() { Int<10> arr[1024]; std::cout << arr[10].val << std::endl; }
https://godbolt.org/z/6-BEPi

четверг, 21 марта 2019 г.

Как обязать использовать 32-битное обращение к структуре?

Есть некая структура с битовыми полями. Допустим
struct _example{ unsigned f10:1; unsigned f11:1; unsigned f2:2; unsigned f4:4; unsigned f8:8; unsigned f16:16; };
При обращении к f10 в виде а10|=1 формируется код orb $1,(%rcx), а нужен orl $1,(%rcx). Регистр %rcx для примера, содержит адрес начала структуры.
Можно соответствующие переменные объявлять volatile, но это решение подходит для самого первого поля.
Если подумать, то компилятор не может определить границы 32-битных слов в структуре, поэтому обращается к байтам. https://gcc.godbolt.org/z/17JQWM


Ответ

Это невозможно. Компилятор не может рассчитать 32-битные границы.

воскресенье, 10 марта 2019 г.

В разница между командами hlt и pause? Что лучше использовать?

hlt "останавливает" процессор до прихода прерывания, что снижает нагрузку. pause тоже "останавливает", но не могу найти конкретного описания. Коды у них разные.


Ответ

Ух. Попробую ответить.
Основная идея, как я понял, в том, что HLT (halt) - останавливает логический процессор, освобождая доступ к общим ресурсам в физическом пакете, которыми могут воспользоваться все активные логические процессоры в этом же пакете.
А PAUSE используется для того чтобы повысить производительность в процессорах с технологией Hyper-Threading. Суть в том что код, который ожидает освобождения чего-либо путем обращения к семафору, находится в wait цикле. Если код выйдет из цикла и обнаружит, что порядок в памяти нарушен (memory order violation), процессор просто сбросит конвейер (pipeline flush). Это приведет к проседанию производительности в целом. PAUSE таким образом нужен для того, чтобы подсказать процессору что кодовая последовательность является wait циклом, и помогает избежать memory order violation и pipeline flush. Также эта инструкция дает какие-то бонусы в энергопотреблении, но я не вчитывался.
Можете почитать еще тут: https://xem.github.io/minix86/manual/intel-x86-and-64-manual-vol3/o_fe12b1e2a880e0ce-302.html

четверг, 4 октября 2018 г.

Как использовать данные о размерах кэшей процессора для ускорения программы?

У каждого процессора есть кэш различных уровней и размеров.
Есть ли смысл писать программу таким образом, что б абсолютно все используемые массивы, переменные и т.п. в оперативной памяти занимали строго последовательные адреса? Т.е. например: начальный адрес 90870000, конечный 90880000. Есть смысл что б этом промежутке были исключительно данные конкретной программы? Как влияет на скорость программы соотношение размера кэшей и используемого размера оперативной памяти? Есть ли смысл обработку данных во много раз больших размера кэшей проводить блоками? Каждый блок меньше размера кэша. В ассемблере есть инструкции по записи в оперативную память без использования кэша. Какой в этом смысл? Какую из двух инструкций и в каких случаях использовать для возврата 128 бит результата обработки обратно в память?
movdqu [ebx],xmm0 movntps [ebx],xmm0//без использования кэша
Правильно я понимаю, что если эти 128 бит больше не нужны для обратотки, то лучше movntps? Это быстрее?
Предположим есть чернобелое bmp изображение 1024*1024. Перенесем все его пиксели в:
unsigned __int8 *src_img src_img = new unsigned __int8[1024*1024];//каждый байт это значение одного пикселя от 0 до 255
Допустим есть два варианта алгоритма: 1) копируем в xmm0 128 бит из src_img, что-то делаем в xmm регистрах и возвращаем измененные 128бит обратно по тому же адресу в оперативную память. Самое важное эти 128 бит выбираются от начала src_img последовательно до конца. 2) Делаем тоже самое, но 128 бит выбираются не последовательно,а из разных мест src_img 1-й вариант будет быстрее или нет? Или формулируя по другому: первоначально программа берет данные из: movdqu xmm0,[ebx]. Имеет различие для быстродействия насколько далеко от первоначального адреса программа берет следующие 128 бит?
Изучаю данные вопросы в контексте вот этой задачи: https://stackoverflow.com/questions/50747393/prewitt-edge-detection-algorithm-using-x86-mmx-simd


Ответ

Есть смысл что б этом промежутке были исключительно данные конкретной программы?
Это зависит от программы. Если она оч. часто обращается к этим переменным, то да, конечно - размещение всех ее данных в блоке, который занимает минимальное к-во кеш-линий, ускорит эти операции.
Обычно не говорят о размещении всех данных в одной кеш-линии. На практике это невозможно. Обычно говорят о "кратном рамере" блока данных.
Как влияет на скорость программы соотношение размера кэшей и используемого размера оперативной памяти?
Особой разницы с первым вопросом не вижу. Если у Вас программа оптимизирована под загрузку кеша, и учитывает его размер, конечно, она будет быстрее работать на железе с большим кешем. Надеюсь, Вы так же вкурсе, что кеши есть разных уровней (level1, level2, level3, кеш данных, кеш инструкций), их размеры могут отличаться, и скорость доступа к ним, естественно, тоже разная, т.е. поле для оптимизаций - обширное настолько, насколько Вам хватит терпения перебирать различное железо для экспериментов и фантазии на реализацию алгоритмов.
Есть ли смысл обработку данных во много раз больших размера кэшей проводить блоками? Каждый блок меньше размера кэша.
Нет. Есть смысл распараллелить эту обработку между ядрами таким образом, чтобы у каждого ядра эти порции данных, по возможности, не пересекались. Вот тут кратность размера обрабатываемого блока данных имеет большое значение. Порции следует разделить так, чтобы кеш-линии одного ядра не пересекались с кеш-линиями другого.
В ассемблере есть инструкции по записи в оперативную память без использования кэша. Какой в этом смысл?
Смысл в том, чтобы не делать лишнюю работу: если Вам нужно только переместить блок данных, и Вы не обращаетесь к нему для каких-то рассчетов, то и в кеш его нет необходимости загружать.
Имеет различие для быстродействия насколько далеко от первоначального адреса программа берет следующие 128 бит?
Нет, не должно. На 64битных кеш-линиях, по крайней мере, последовательное чтение 128битных значений в непараллельной программе выгоды не дает. В этом случае определяющим будет размер кеша и параллелизм между ядрами (кооперация кэшей). Ждите или ищите 256битных кеш линий, по крайней мере :)

Как использовать данные о размерах кэшей процессора для ускорения программы?

У каждого процессора есть кэш различных уровней и размеров.
Есть ли смысл писать программу таким образом, что б абсолютно все используемые массивы, переменные и т.п. в оперативной памяти занимали строго последовательные адреса? Т.е. например: начальный адрес 90870000, конечный 90880000. Есть смысл что б этом промежутке были исключительно данные конкретной программы? Как влияет на скорость программы соотношение размера кэшей и используемого размера оперативной памяти? Есть ли смысл обработку данных во много раз больших размера кэшей проводить блоками? Каждый блок меньше размера кэша. В ассемблере есть инструкции по записи в оперативную память без использования кэша. Какой в этом смысл? Какую из двух инструкций и в каких случаях использовать для возврата 128 бит результата обработки обратно в память?
movdqu [ebx],xmm0 movntps [ebx],xmm0//без использования кэша
Правильно я понимаю, что если эти 128 бит больше не нужны для обратотки, то лучше movntps? Это быстрее?
Предположим есть чернобелое bmp изображение 1024*1024. Перенесем все его пиксели в:
unsigned __int8 *src_img src_img = new unsigned __int8[1024*1024];//каждый байт это значение одного пикселя от 0 до 255
Допустим есть два варианта алгоритма: 1) копируем в xmm0 128 бит из src_img, что-то делаем в xmm регистрах и возвращаем измененные 128бит обратно по тому же адресу в оперативную память. Самое важное эти 128 бит выбираются от начала src_img последовательно до конца. 2) Делаем тоже самое, но 128 бит выбираются не последовательно,а из разных мест src_img 1-й вариант будет быстрее или нет? Или формулируя по другому: первоначально программа берет данные из: movdqu xmm0,[ebx]. Имеет различие для быстродействия насколько далеко от первоначального адреса программа берет следующие 128 бит?
Изучаю данные вопросы в контексте вот этой задачи: https://stackoverflow.com/questions/50747393/prewitt-edge-detection-algorithm-using-x86-mmx-simd


Ответ

Есть смысл что б этом промежутке были исключительно данные конкретной программы?
Это зависит от программы. Если она оч. часто обращается к этим переменным, то да, конечно - размещение всех ее данных в блоке, который занимает минимальное к-во кеш-линий, ускорит эти операции.
Обычно не говорят о размещении всех данных в одной кеш-линии. На практике это невозможно. Обычно говорят о "кратном рамере" блока данных.
Как влияет на скорость программы соотношение размера кэшей и используемого размера оперативной памяти?
Особой разницы с первым вопросом не вижу. Если у Вас программа оптимизирована под загрузку кеша, и учитывает его размер, конечно, она будет быстрее работать на железе с большим кешем. Надеюсь, Вы так же вкурсе, что кеши есть разных уровней (level1, level2, level3, кеш данных, кеш инструкций), их размеры могут отличаться, и скорость доступа к ним, естественно, тоже разная, т.е. поле для оптимизаций - обширное настолько, насколько Вам хватит терпения перебирать различное железо для экспериментов и фантазии на реализацию алгоритмов.
Есть ли смысл обработку данных во много раз больших размера кэшей проводить блоками? Каждый блок меньше размера кэша.
Нет. Есть смысл распараллелить эту обработку между ядрами таким образом, чтобы у каждого ядра эти порции данных, по возможности, не пересекались. Вот тут кратность размера обрабатываемого блока данных имеет большое значение. Порции следует разделить так, чтобы кеш-линии одного ядра не пересекались с кеш-линиями другого.
В ассемблере есть инструкции по записи в оперативную память без использования кэша. Какой в этом смысл?
Смысл в том, чтобы не делать лишнюю работу: если Вам нужно только переместить блок данных, и Вы не обращаетесь к нему для каких-то рассчетов, то и в кеш его нет необходимости загружать.
Имеет различие для быстродействия насколько далеко от первоначального адреса программа берет следующие 128 бит?
Нет, не должно. На 64битных кеш-линиях, по крайней мере, последовательное чтение 128битных значений в непараллельной программе выгоды не дает. В этом случае определяющим будет размер кеша и параллелизм между ядрами (кооперация кэшей). Ждите или ищите 256битных кеш линий, по крайней мере :)