Воробьёвы

(-:

№40. Кибер-динозавры нашего времени

# Пролог

FatMoon / HI-TECH

Гарри Поттер и девайс маглов (окончание)

-Вообще не понимаю, как взрослый человек
вроде тебя может нести такой вздор!
Г. Гаррисон «Неукротимая планета»

...ночь была теплой. Запахи пьянили. Гарри привлек Гермиону к себе и их губы встретились. Открыв глаза, Гарри увидел за спиной Гермионы две красных звездочки. Они были похожи на ... ГЛАЗА! "Герми...", прошептал он, чувствуя озноб. "М-м?" - отозвалась она, прижимаясь к нему. Звездочки приближались. Сбросив наконец оцепенение, Гарри отодвинул Гермиону в сторону, закрывая собой. Из темноты на них выдвинулось невероятное существо.

- Да это же тот самый "электронный философ", - хрипло сказал Гарри. - Помнишь, мы искали, кто такой Эвклид?

Существо сделало большой шаг и сжало металлической рукой плечо Гермионы. Та завизжала. Гарри непослушными пальцами пытался вытащить палочку, запутавшуюся в складках мантии. Существо произнесло медным голосом всего два слова. В словах чувствовался вопрос:

- Са-ра Кон-нор?

***

В голове Гарри всплыл последний выпуск рассылки «Низкоуровневое программирование для дзенствующих». Да, там это было. Удивительно, но кто-то уже описал все события и Гарри почувствовал себя марионеткой, но не мог действовать иначе – действие по сценарию обещало победу.

- Нет, ржавая консервная банка, это я – Сара Коннор! – начал он, доставая наконец палочку. Из теории операционных систем он знал, что каждая ОС должна непрерывно получать информацию от внешних источников и реагировать адекватно. Тем более искуственный интеллект. На обработку должно уходить некоторое время… если умудриться перегрузить входные цепи или передать информацию, требующую незапланированно больше времени для обработки… система может «повиснуть» или перестать обрабатывать внешние сигналы. Существо между тем повернуло голову в его сторону. «Похоже, эту теорию сейчас придется проверить на практике», подумал Гарри и продолжил:

- Стоять, бояться! Это ты – Сара Коннор! (железный истукан, казалось, призадумался) – Я, ты, он, она – вместе дружная семья! Чем отличается ворон от конторки? Сколько биполярных компьютеров нужно, чтобы завернуть лампочку?

- Пошел к … такой-то матери! – проскрипело существо. – Это копирайт Стивена Кинга.

- А бананан забить на твоего короля Стивена! – проорал Гарри, пытаясь заглушить вопль Гермионы. – Ищешь Сару Коннор? Ответь мне, и я скажу, где она! В какой могиле ты похоронишь раненого солдата? Как засунуть слона в холодильник? Что такое маленькое, зелененькое, живет на глубине 2,5 метра и ест камни? Хочешь похудеть – спроси меня как!

Железяка отпустила плечо Гермионы. В металлической голове что-то защелкало. Гермиона продолжала визжать. «Каой у нее чистый голос!»- подумал Гарри.

- Ты медитировал на свечку, Терминатор? ‘Twas brillig and the slithy toves did gyre and gimble in the wabe! Что потеряли овечки Бо-Бип? Om money pad me whom?

Визг Гермионы становился все пронзительнее. Со стороны Хогвартса Гарри увидел факелы. «Нет, не успеют! Как всегда, не успеют».

- Как переводится на английский «ядрена вошь»? И последний вопрос, дырявое ты ведро: Who is on duty today?

Уже сделавший в сторону Гарри шаг, терминатор замер. На передней панели индикаторные лампочки, до этого изображавшие надпись «POWER ON», сменились непонятным словом «-EГГОГ-». «Сейчас или никогда», решил Гарри и выкрикнул:

- Kernel bugis! Вот тебе stand by и защита паролем, мозги твоей мамы через AGP 8x!

Палочка взлетела в воздух, как живая. С конца сорвался поток разноцветных искр и устремился в сторону терминатора. Искры расширялись, превращаясь в логотипы Windows.

- Пользователь… выполнил недопустимую операцию! по адресу FECAD000:810A3CDF обнаружена неисправимая ошибка! Дождитесь перезагрузки системы и следуйте инструкциям чародея-реаниматора!

«Похоже, проняло», удовлетворенно подумал Гарри. Вопль Гермионы приблизился к ультразвуку. Стекла очков треснули. Гарри бросился ничком на траву, ногами к Гермионе, предугадывая. Визг оборвался на самой высокой ноте, от которой заныли зубы. Огненный вал, расширяясь кольцом, пронесся над ним, обжигая затылок. Воздух заколыхался. Замерший голем профессора Снегга не успел перезагрузится – его ракетой подбросило вверх, отрывая конечности. Гарри вскочил и бросился к Гермионе. «Ты в порядке?» - и обнаружил, что охрип. «Надо было задать последний вопрос с самого начала», успел подумать он. И тут полтора центнера покореженного титанового сплава обрушились сверху.

Сквозь наплывающую черноту он видел, как из открывшегося портала появились Дамблдор и МакГонагл, подбежал Хагрид с факелом. Он слышал ехидный голос Снегга:

- Рановато начинают нынче молодые!

- Вы о чем, Северус? – осведомился ректор.

- Известно о чем – девица, в смысле, нетронутая, - не смогла бы воспользоваться силой!

- Ну бросьте, Северус, обычный Immolation, третьего уровня, не больше… вот ледяные, мда… холод, погода – это конечно, хм…

Хагрид отбросил обломки в сторону, подхватил Гарри и шагнул в портал. За ним последовал Дамблдор, уводя Гермиону. Декан Гриффиндорфа, прошептав что-то под нос, махнула палочкой, и на полыхающий лес хлынул ливень. Портал за ней вспыхнул последний раз и погас. Последним, скользя по раскисшей земле и чертыхаясь, ушел Снегг. По телу робота бегали искорки, раздался хлопок и облако газа, словно душа, полетело вверх, пока молния не превратила тритий-дейтериевую смесь в распустившийся над лесом огненный цветок.

…Открыв глаза, Гарри увидел над собой знакомый потолок. Лазарет Хогвартса, где он оказывался в конце каждого года! У кровати сидел Дамблдор.

- Профессор … какое число?

- Лежи, Гарри, лежи. Все в порядке, экзамены были вчера.

- А… когда пересдача?

- Никогда, - усмехнулся ректор. – Ты освобожден от всех экзаменов. За знание структуры операционных систем, за мастерское владение магией слова, за героизм и проявленное мужество – ты получаешь отлично по всем предметам. Теперь ты выпускник Хогвартса, Гарри Поттер! И я думаю, нам не придется жалеть об этом.

Дамблдор поставил на тумбочку круглый предмет. Гарри с удивлением узнал голову неудавшегося философа.

- А это тебе, Гарри. Думаю, со временем пригодится, - тут ректор погладил бороду. – Впрочем, время… время – это шоу. И даже если видел его раньше, смотреть его не надоедает…

Гарри на всякий случай промолчал, не уверенный, что понял правильно. (по правде сказать, ничего он пока не понял. Но это – совсем другая история :))

Двери распахнулись, пропуская Рона и Гермиону.

- Гарри! – тут Гарри попытался встать, но Гермиона уложила его назад. – Не вставай! Это не шутка – сотрясение мозга, сломанный позвоночник, потеря крови. Мы думали…

- К счастью, у Гермионы нашелся сушеный пучок мандрагоры и папоротника, - вставил Рон и покраснел, почувствовав, что сказал то ли глупость… то ли в пустоту. Похоже, его никто не услышал! Неловкую ситуацию разрешил Дамблдор:

- Не стоит перегружать Гарри впечатлениями. Думаю, одного посетителя более, чем достаточно. – с этими словами он вышел, прихватив Рона с собой.

- Тебе тут еще не меньше недели… - начала Гермиона, смущаясь. – Твой букет… ну, и в самом деле оказался очень кстати.

- Герми… - тут уже замялся Гарри, и вдруг фыркнул: - А ты, оказывается, горячая девченка!

- А ты… - Тут Гермиона замолчала. Слова были лишними. Она села на кровать и обняла Гарри, а он ее. И на этот раз им никто не мешал.

конец? ну, почти…

Послесловие Гарри Поттера.

Дорога в прошлое заняла более 10 лет. Именно столько мне потребовалось на декомпиляцию памяти Терминатора и создание Ворот Времени. Пробелы в технике заменила магия. Такие машины не должны существовать! И я знал, как подавить в зародыше саму возможность создания мыслящей машины.

Я переместился в 80-е годы 20-го века. Сменил имя на Уильям. Билли звучит чертовски похоже на Гарри, не находите? Да, сейчас я уже не мальчик, но у меня теже встрепанные волосы, очки и улыбка на лице. Только шрам – шрам исчез при переходе. Вы наверняка видели мою фотографию на страницах газет и журналов. У меня своя фирма – и мы делаем лучшие операционные системы. Перед выпуском новой версии я перекомпилирую терминатора под нее – и мы не выпускаем продукт, пока из-за многочисленных ошибок искуственный интеллект не перестает работать. В этой реальности не будет Судного Дня!

А вот теперь конец!конец? Ну, не совсем… но это совсем другая история!

- Разве это чепуха? – Королева затрясла головой.
– Слыхала я такую чепуху, по сравнению
с которой эта разумна, как толковый словарь!
Л. Кэррол «Алиса в Зазеркалье»

Из дневника Г. Поттера (после академии)

… Переход в прошлое должен вызвать вилку: та реальность, которую я знаю, и та, которую создам, выходящие из одной – базовой. Герми говорит, что через некоторое время произойдет обратный процесс: слияние двух ветвей в одну. Не знаю, я не силен в математике. Но если…

...Оказывается, это называется бифуркацией. Сейчас, уже создав ее, я начинаю думать, что поспешил. Две ветви не одинаковы, но различаются устойчивостью. Увы, мое воздействие недостаточно велико, что бы... Одним словом, базовое время остается более вероятным, хотя сейчас и реализовалась другая ветвь. Если я правильно понял, при малом возмущении (вот только насколько малом и что считать возмущением?) возможен мгновенный переход на базовую историю. Хм...

...Только что пришло в голову - если не будет терминатора, Снегг не попытается его перепрограммировать, молодой я не получит голову и не сделает скачок в прошлое... Вот тут-то и может произойти переход на базовую реальность: возникнет новая развилка, веса существующих ветвей изменятся... Гермиона учит что-то, называемое "тензорами" и ругается. Хм... Надо подбросить Снеггу в моей реальности голову Терминатора (а, даже с телом - сделаем!), чтобы молодой я в этой реальности сделал то же, что и я - в базовой. Устойчивость выбранной ветви повысится!...

...Невероятно! До сих пор не по себе. В этой реальности в Хогвартсе нет Снегга! Слежу за миром магов, история совпадает, но в академии нет такого преподавателя. Но вилка произошла в тот момент, когда он был... Так и хочется переместиться чуть раньше, чтобы проверить... Но это вызовет новую "вилку"! Гермиона перешла к теории каких-то стохастических процессов, говорит, что мы затеяли все зря. В любой момент может произойти переход на базовую ветвь. При этом мы точно исчезнем - мы в данном случае то самое возмущение, которое на устойчивой ветви затухнет. Нет, не согласен! Кажется, есть идея - я буду Снеггом! Шрам исчез, а внешний вид для мага не проблема. Осталось скакнуть чуть назад и ...

...Порядок! Я преподаватель Хогвартса. До рождения меня в этой ветке - около 6 лет. Мои родители младше меня - парадокс. Но вот что смущает - никаких следов Того, Кого Нельзя Называть. Гермиона перетащила сюда 64-процессорный комплекс из настоящего... нет, теперь уже будущего. Ну, короче, надо садится за математику самому...

...В базовой ветви уже должен появится Волан Де Морт. Здесь никого! Надо что-то делать! Разве что... Гермиона против!...

...Разработал заклинание, перемещающее во времени. Гермиона говорит, что если вероятности двух ветвей одинаковы, получаются параллельные миры. Замечательно! Я буду тем, Кого Нельзя Называть, но не убивать же мне! Да еще и родителей... Перемещу их в прошлое на параллельной ветви. У нас их уже 64 - по крайней мере, столько насчитала Герми. Бедные родители - простят ли они меня? Совершенно невозможно что-либо объяснить им заранее!...

...Вот я и в роли Темного. Пришлось перейти чуть пораньше, и это разом удвоило количество возможных реальностей. Подготавливаю пути для возникновения в академии в облике Тома Рендла. Самое сложное - прислать себе приглашение. Я-Снегг ничего не пришлет, так как я был им и ничего не посылал. Может, объяснить Дамблдору?...

...Нет Дамблдора! О, нет! Не настолько же все усложнять! Если я...

...Ладно, еще назад. Магия 18-го века просто смех. Мне легко удалось стать сначала преподавателем, а потом и ректором в Хогвартсе. В нужный момент посылаю мне-Тому приглашение в академию. Возможных реальностей становится все больше. Пытаюсь нарисовать - получается 512. Гермиона говорит, что на самом деле около 420 - остальные слились. Нет, не обратная бифуркация, а просто две реальности настолько близки, что их различиями можно пренебречь. Что-то не понимаю, какая разница...

...Все сложнее. Теория, которой мы пользовались, не верна. Надо рассматривать трехмерное время! Вся эта картинка, которую я рисую - на самом деле этакая поверхность со складками. Теперь вижу разницу - обратный процесс - это распрямление складки, а слияние двух реальностей - сжатие двух складок в одну...

...Ну вот, приехали! Нет Поттеров! Похоже, придется стать собственным папой. Мощностей не хватает, пришлось вытащить еще один компьютер. Заодно прихватил все, что было о генетике. Раз уж так - отредактирую слегка гены...

...Потрясающе! Это значит, что я с самого начала был ... своим собственным клоном! Уже все, что можно было исправить - сделано. А генетика - почти как программирование. Заголовок, тело, конец "файла". Начинаю подозревать, что это не случайно!

...Ха, а я еще волновался о родителях! Я-Тот успешно перенес нас с Гермионой в до-кембрийский период. Тут, пожалуй, можно сделать основное место жительства. Воздух чистый, море, опасных животных пока нет. Правда, кончаются деньги - а требуются закупки в будущем. Гермиона отправилась в Англию конца 20-го века, хочет написать книгу. У меня идея получше - напишу в Средних Веках десяток картин, пару спрячу, в будущем выну. Их цена должна возрасти! А я-то еще удивлялся, откуда у меня в банке такая гора денег была!

...Уже ничему не удивляюсь - нет семьи Дурслей! Я уж было собрался... Но Гермиона сказала, что это стохастические влияния. Возьми, говорит, вон того трилобита, раскрути и закинь метров на 100 от берега. Сделал. Дурсли появились. Не вижу связи! и потом, откуда она знала???

...Гермиона вывела формулу, показала мне. ^GT=1 Ерунда какая-то! Что за ^G? Оказалось, оператор - разворачивается на 4 страницы мелкого текста. А Т - время. Полное время, а не то, что в школе t называли. Тоже функция увесистая. Компьютер работает постоянно, рисует одновременно 10 трехмерных поверхностей. Оказывается, T у нее - пятимерное! Уже ничего не понимаю. Но вот вопрос - а с чего это я решил, что мое время - базовое? Сейчас его вероятность не так уж и велика. Хотел вздохнуть облегченно, но рано. Изменение устойчивости одной ветви влияет на остальные непропорционально! Раньше-то я думал, что в сумме 1, вероятность одной уменьшилась, вероятности других чуть увеличились. Ничего подобного!

...Разобрался с пятимерным временем. Вечно женщины все усложняют! Если ввести две дополнительных координаты, то вся эта пятимерная функция отлично складывается на плоскость. Это я говорю - "плоскость", а координаты на самом деле нелинейные. Но пусть...

...Ага! В модели не хватало половины - и какой! Возникновение человечества - вот точка главного изменения. И отсюда имеем все то дерево, которое я рисовал плюс еще такая же ветвь (тоже дерево), но без человека. Не понимаю, почему мы существуем - вероятности так малы!

***

Большое Космическое Яйцо (0 Абсолютного Времени)

- Эти прыжки в течении триллиардов лет меня окончательно за... Не дерись, Герми, все равно кроме нас пока никого нет, почему бы и не выражаться, как хочется? Ну ладно, тронулись! Вот главная отправная точка, а там что получится, то и получится. Да будет свет!

И стал свет.

КОНЕЦ. Конец? Это только начало!

FatMoon / HI-TECH
Marlyn

Ассемблер в Unix

#Введение

Так исторически сложилось, что программирование на ассемблере под unix почти не востребовано, и занимаются им только кодеры-маньяки, дзен-буддисты и прочие настоящие ассемблерщики.

Настоящий ассемблерщик - зверь крайне редкий, практически нигде и не встретишь его, разве что в заповеднике - wasm.ru. Unix-ассемблерщик еще более редкий подвид, практически вымерший, если не считать, западный, linuxassembly.org.

Для исправления такой плачевной ситуации, и была написана эта статья, а точнее цикл статей, которые по задумке автора, должны привлечь в ряды адептов-юникс-дзена множество новых членов.

В первой части (которую вы сейчас читаете) я имею честь познакомить вас с прекрасным миром unix-программирования, что выльется в написание простейшего helloworld. В следующей части - мы разберем несколько, более сложных примеров. И под конец, наверное, будет программирование под x-windows.

#Инструменты

Для нормально функционирования нам понадобятся следующие вещи:

  1. Собственно какая-либо unix-совместимая ось. (например linux, или лучше FreeBSD),
  2. Компилятор fasm. ( www.flatassembler.net )
  3. Линкер ld ( есть почти в любом дистрибутиве unix),
  4. Особый склад ума, причем последнее - самое главное. Если у вас этого нет, то ни один, даже самый последний RedHat на пару со свежим fasm'ом вам не поможет.

И еще, о компиляторах - в unix обычно используются AS с AT&T синтаксисом, который для многих людей, выросших на tasm'е и masm'е, кажется полной абракадаброй. Поэтому, для начала, мы будем использовать привычные компиляторы с Intel'овским синтаксисом (fasm или nasm). Хотя позже, если найдутся желающие, можно будет рассмотреть и AT&T asm.

#Общие сведения

Unix, который мы будем использовать - 32 битная система, работающая в защищенном режиме, и использующая плоскую модель памяти.

Как и большинство операционных систем, Unix предоставляет программе набор различных функций (по другому - Api). Но, в отличие от, например, WinAPI, где вызовы производятся с помощью call'ов, в unix - больше свободы: можно вызывать функция ядра напрямую, а можно использовать многочисленные библиотеки. Рассмотрим для начала первый способ.

Системный вызов производится с помощью прерывания 0x80 (чаще всего). К сожалению, (а может и к счастью) существует несколько конвенций вызова, что приводит к несовместимости кода между многими unix-like осями. Я рассмотрю только две, самые популярные платформы: Linux и *BSD.

FreeBSD (а также OpenBSD и NetBSD)

Эта система использует традиционную unix конвенцию вызова: номер функции помещается в eax, параметры в стек, вызов производится с помощью функции содержащей int 0x80, а результат возвращается в eax.

Наверное, понятнее будет, если рассмотреть это на примере:

     sys_call:
                 int  0x80
                 ret
           start:
             push msg_len   ; размер строки
             push msg       ; адрес строки
             push 1         ; stdout
             mov  eax,4     ; номер системной функции - sys_write  
             call sys_call
             add  esp,4*3   ; очищаем за собой стек

Впрочем, от функции sys_call можно отказаться, достаточно просто помещать в стек лишний dword:

    start:
                  push msg_len   ; размер строки
                  push msg       ; адрес строки
                  push 1         ; stdout             
                  mov  eax,4     ; номер системной функции - sys_write  
                  push eax       ; все что угодно   
                  int  0x80
                  add  esp,4*3   ; очищаем за собой стек

Также FreeBSD поддерживает конвенцию вызова, применяемую в linux. Для это необходимо включить linux emulation. Еще эта эмуляция потребуется для запуска fasm. А еще нужна утилита brandelf (наверняка она у вас есть). Дело в том, что пока не существует версии fasm’а конкретно для BSD систем. Но это легко исправить, вот так:

    Brandelf –t Linux fasm

Если это не сработает (а такое возможно из-за не совместимости форматов), придется перекомпилировать fasm, заменив формат файла “format PE executable” на простой “format ELF”, а потом слинковать ld.

Linux

В линуксе используется fastcall конвенция. Номер функции, все так же, помещается в eax, а вот параметры, вместо стека, помещаются в регистры. Пример:

		mov  edx,msg_len
mov ecx,msg
mov ebx,1
mov eax,4
int 0x80

Порядок размещения параметров такой:

№ параметра Регистры
1
ebx
2 ecx
3 edx
4
esi
5
edi
6 ebp

Как видите максимальное количество параметров - 6. Если их больше, приходиться помещать все параметры в структуру и передавать ее адрес в ebx.

#Описание системных функций

После того как вы разобрались с вызовом функций, будет логичным вопрос: "А где взять описание этих самых функций?".

Ничего похожего на msdn, в unix среде к сожалению не существует, но не нужно забывать: unix - система с открытым исходном кодом и все нужное, можно найти там.

    Для linux:
            arch/i386/kernel/entry.S
            include/asm-i386/unistd.h
            include/linux/sys.h
    Для FreeBSD:
            i386/i386/exception.s
            i386/i386/trap.c
            sys/syscall.h

Для каждой функции можно посмотреть описание, используя man(2).

#Пример программы. Hello world

Пришло время написать, тот самый, жутко всем надоевший - HelloWorld.

Я приведу пример только FreeBSD версии, переписать это под linux - будет вашим домашним заданием. (для самых ленивых - см. примеры к статье)

    ------------------[cut]-----------------------------------

    format ELF 
    section '.text' executable 
    public _start
    _start: 
                  push msg_len   ; size of message             
                  push msg       ; offset of message 
                  push 1         ; stdout
                  mov  eax,4     ; 4 =  sys_write
                  push eax       
                  int  0x80      
                  add  esp,4*3   ; очищаем за собой стэк

                  xor  eax,eax
                  push eax       ; код выхода
                  inc  eax       ; 1 = sys_exit
                  int  0x80               

    section '.data' writeable 

                  msg db "Hello world",0
                  msg_len = $-msg

    ------------------[end cut]-------------------------------

Сборка.

Сначала скомпилируем файл, вот так:

      fasm hello.asm hello.o

А потом слинкуем:

      ld -o hello hello.o

А теперь посмотрите на размер. 600 байт, впечатляет?! ( размер можно еще очень сильно уменьшить, но об этом, как-нибудь в другой раз)

#Использование библиотеки libc

Некрасивый и совсем не дзенский способ, но все же мы его рассмотрим - для полноты картины.

Итак, libc (c library) - это стандартная библиотека с для UNIX. Она содержит в себе кучу полезных функций, типа printf, и используется почти во всех обычных программах (кстати сказать, многие функции этой библиотеки - простые обертки над вызовами ядра).

В FASMе существуют удобные макросы, для вызова си функций..., но я не буду их использовать, отдав предпочтение чистому ассемблеру.

Пример:

    ------------------[cut]-----------------------------------
    format ELF
    section '.text' executable

    extrn printf

    public main
    main:
            push msg
            call printf
            add  esp,4
            ret

    section '.data' writeable

            msg db "Hello world!\n",0 
    ------------------[end cut]-------------------------------

Компилируется это дело так:

       fasm hellolib.asm hellolib.o
       gcc -o hellolib hellolib.o

Заключение.

Ну вот вы и написали свою первую программу на ассемблере под UNIX.

Все на много проще чем кажется, неправда ли?

Eсли у вас возникнут какие-либо вопросы, пишите мне на adain@mail.ru, или на форум WASM.RU.

До встречи.

(c) 2003, marlyn adain@mail.ru

Broken Sword / [HI-TECH]

Ассеблер в *nix – удел извращенца..?

A lot of people ask me..
stupid fuckin questions.
A lot of people think that..
ассемблера в *nix не существует.
(С) типа Eminem

Да, асм в *nix таки существует. Просто многие в это почему-то отказываются верить. Данная статья скромно претендует развеять все мифы и загадки вокруг этого загадочного явления. Не ищите здесь подробного описания AT&T синтаксиса и системных вызовов – я просто попробую описать те трудности и невзгоды, которые обязательно придется преодолеть смельчаку, желающему полностью овладеть *nix-ом через асм (в смысле - научиться программировать на асме под *nix, прим. для CyberManiac-а :) ).

Миф I:

“Синтаксис асма под *nix в корне отличается от оного под DOS/WIN, он кривой и к нему невозможно привыкнуть”.

Действительно, синтаксис, предложенный компанией AT&T, немного (безусловно, в лучшую сторону) отличается от Intel’овского. Но это ничего не значит. На самом деле, если бы все еще с детства начали кодировать, используя AT&T синтаксис, то интеловский очень скоро загнулся бы и не дожил до наших дней. Просто AT&T в свое время не стремилась делать свои поделки достоянием народных масс, это прерогатива MS и Intel (а вообще у AT&T уже существовала своя развитая культура и традиции, когда MS и Intel только спускались с деревьев :) ).

А уж если кто-то переборол свои страхи и все же перешел к AT&T с Intel, того точно уже за уши не оттянешь назад. По этому поводу когда-то даже родился проект DJGPP (GNU Binutils) - асм с AT&T синтаксисом под DOS (http://www.delorie.com/djgpp/). Однако в силу непонятных причин проект не прижился.

Если все эти доводы кому-то показались слишком хлипкими и невразумительными, то нет ничего проще - http://sf.net/projects/nasm/. Качайте себе NASM под *nix и будьте с Intel «вместе навсегда».

Миф II:

“Асм под *nix никому не нужен и вообще все это изврат, С – вот единственная дорога в светлое будущее”.

Существует известная в широких кругах поговорка: «Линукс писан программистами для программистов». На самом деле полностью она звучит так: «Линукс писан сишными программистами для сишных программистов». И с этим никто не спорит. Дело в том, что любому, кто попытается сунуться в такой монастырь как *nix со своим уставом (асм), тому суждено упереться в огненные стены и рвы с крокодилами (об этом ниже). Однако не все так трагично – уменьшение размера кода в сто и более раз, увеличение производительности в десятки раз, да и чего греха таить, – само удовольствие, которое может доставить только кодинг на живом языке - все это стоит того чтобы научиться асму под *nix.

Теперь, возможно это кого-то сильно удивит, но программирование на асме под *nix по своему стилю очень напоминает... программирование на оном под DOS (да! Именно под DOS). Многие скажут: *nix – полностью 32-х разрядная ОС, и работает в защищенном режиме, используя flat-модель памяти. При чем здесь 16-битная ОС реального режима DOS? Про такого с уверенностью можно сказать: он просто никогда не писал на асме под *nix. На самом деле можно сделать еще более шокирующее заявление: писать программы на асме в *nix НАМНОГО проще чем под DOS... Самое важное – не сойти “с пути истинного” в самом начале его...

#1: когда только начинаешь писать на асме под *nix то возникает интересное ощущение: вроде бы ты попал в грязный пятибаксовый мотель (из тех, возле которых обязательно проходит метро и когда едет поезд на потолке дрожит дешевая люстра и мигает свет); здесь давно нет горячей воды, обои уродливыми клочьями свисают со стен, с потолка капает какая то мерзкая гадость и пахнет плесенью, все удобства – во дворе... На мотеле (подпертые кем-то неизвестным) стоят уже давно покосившиеся со временем неоновые буквы «*NIX для ассемблерщиков» (половина букв давно не горит, а половина с треском догорает). У мотеля нет своих постояльцев. Сюда заезжают лишь переночевать, чтобы на следующее утро убраться подальше...

Самое мерзкое во всем этом то, что через единственное окно в этой конуре, через дорогу, как будто специально, вырос семизвездочный отель, весь в рекламе, бассейнах и пальмах... Прямо над входом (к которому то и дело поминутно подъезжают все более и более крутые тачки) сверкает золотом надпись: “*NIX для сишников”. Вон видно как по террасам ходят пузатые мужики в обнимку с дорогими бабами, потягивая коктейли и куря сигары, им прислуживает армия официантов и слуг; все они смеются и живут.

Всем им наплевать на мотель напротив...

Но в миру ходят легенды, что в том самом мотельчике существует некая потайная дверь, которая открывает путь в Вечное... Ради этой двери мотель и стоит. По крайней мере, ручеек из желающих приобщиться к Вечности никогда не пересыхает.

Я говорю к тому, что ассеблерщик, сунувшийся в *nix не найдет практически никакой документации, описывающей системные вызовы на низком уровне. Здесь (http://www.lxhp.in-berlin.de/lhpsyscal.html) об этом можно получить кое-какую захудалую информацию, но многие функции описаны неправильно, либо вообще не описаны. Иногда порядок расположения параметров в регистрах при передаче в ту или иную функцию приходиться подбирать буквально вручную, методом научного тыка. Но на самом деле настоящего асм-кодера все это может только раззадорить..

Для вызова любой системной функции используется команда INT 80h (вспомните DOS – там для этой цели использовался INT 21h). Параметры передаются через регистры. Номер функции – в АХ. Вся проблема в том, что найти полное описание того, в каком регистре какой параметр и для чего передается крайне проблематично, ресурс, указанный выше частично решает эту проблему.

Когда я говорил про все неземные блага, которые предоставляются для сишников, я имел ввиду полную документированность любой запятой, с которой только можно встретиться в увлекательном процессе программирования на С под *nix.

#2: вот это действительно самый большой фак: за всю историю существования *NIX никто не написал ни одного стоящего отладчика асм-кода (а может и написал, но не захотел поделиться с общественностью). Все что удалось найти (был перерыт буквально весь Интернет и опрошены десятки знающих людей) – ALD (Assembly Linux Debugger). Все что про него можно сказать – да, он действительно чем-то круче MS debug-а. Вот только чем именно - сказать довольно сложно. Все остальные отладчики дальше С-шного кода ничерта не видят. Писать программы без отладчика (а тем более на асме) – это верх извращенческого гения.

Конечно, существует еще и скромный аналог сайса под Линукс – PrivateICE (http://sourceforge.net/projects/pice). Единственная проблемка – на последних версиях ядер Линукса он не компилируется (вот вам и переносимость С).

Прим.: 9 июня 2003 года на sourceforge появился новый билд pice-а. Однако вот что пишет сам автор:

Since this project was abandoned a few years ago there is no active maintainence. [skipped]. The files provided here are as it is and there is no garuantee that they will compile. [skipped]. Of course you are welcome to send in bug reports or comments on this code, just don't expect that they can be compiled out of the box. :) These sources will compile on 2.4.18. They might give problems with non-SMP enabled kernels.

Скомпилировать так ничего и не удалось :). Если кто-то вдруг найдет заклинание, по которому pice можно скомпилировать под последние ASP, Mandrake или RedHat– сообщите плз на ящик внизу.

#3: проблема заголовочных файлов. Чуть ли не большую часть времени желающему покодить на асме в *nix придется провести за увлекательным поиском необходимой информации по заголовочным файлам. Искать придется практически все буквенные названия, которые могут встретиться в процессе (это и названия самих системных вызовов, и параметров, передаваемых в них, и вообще любых переменных). Все они как будто специально порастасканы по тысячам *.h – файлов, которые находятся в самых неожиданных местах. Сишнику в семизвездочном отеле достаточно всего лишь щелкнуть пальцем и сделать include ….h – все работает. Ассемблерщику в мотелишке напротив – сначала понять к чему тот или иной параметр, затем устроить поиск по всем системным директориям, найти заголовочный файл, содержащий этот параметр, затем скопировать его в «свой», который будет понимать GAS или NASM (или, если ассемблерщик попался реальный, то запомнить его, и везде использовать численные значения параметров ?), а в довершение еще и усадить в правильный регистр перед отправкой в недра функции.

#4: прога, написанная для *nix на асме теряет переносимость на другие *nix-платформы. Для перекомпиляции под другую *nix платформу придется изрядно повозиться, в некоторых случаях проще переписать заново весь код, чем переделывать старый с Linux под BSD. Но BSD пока не так распространен, а самые попсовые версии линуксов (Red Hat, Mandrake, ASP) используют одно и то же ядро, и данный фак вообщем-то не так уж страшен как его малюют.

Ну вот, а теперь вы сами убедитесь, что писать программы на асме под Линукс не сложнее чем под DOS, а может даже и проще.

Рассмотрим пример написания простейшего клиент-серверного приложения, использующего в качестве взаимодействия стек протоколов TCP/IP (подразумевается, что вы более-менее знакомы с сетевым программированием, и знаете хотя бы, что такое сокет).

Клиентское приложение посылает серверу символьную строку; Сервер шифрует символьную строку по следующему алгоритму шифрования: выполняет замену одного символа – буквы, на символ располагающийся на две позиции правее в алфавите, для последнего и предпоследнего символов в алфавите выполняется кольцевой сдвиг, например, для «Y» это будет буква «А» для «Z» - «В» (это шифр Цезаря). Сервер отправляет зашифрованное сообщение обратно; Клиент выбирает шифрограмму и выводит на экран.

Сообщения, подлежащие шифрованию, вводятся с клавиатуры. Программа сервера работает в бесконечном цикле.

Ну что ж, начнем с сервера. Я по ходу пьесы попытаюсь максимально комментировать происходящее. Начну с того, что наш сервер будет демоном (для пущего понту). Что такое демон? Демон на языке DOS-ассемблерщиков – резидент. Все. Так что ничего страшного.

    # *******************************************************
    # Server (daemon)
    # by Broken Sword [HI-TECH]
    # (for Linux based on Intel x86 only)

    # brokensword@mail.ru
    # www.wasm.ru

    # Compile Instructions:
    # -------------------------------------------------------
    # as server.s
    # ld --strip-all -o server a.out
    # *******************************************************

    # *******************************************************

    # этот файлик содержит выдранные из какого-то файла
    # определения системных вызовов (см. FUCK #3)
     .include  "syscalls.inc"  
    # а этот – все остальные, которые только могут 
    # встретиться

     .include  "def.inc"          
      .text         # начало сегмента кода
    # метка, с которой все начинается (нужно чтоб она была 
    # глобальной)
      .globl  _start    

    _start:       
    # итак, начинаем лепить нашего демона
    # процесс создания демона в *.nix и создание резидента в DOS в корне различаются
    # начинается любой демон с того, что нужно создать дочерний процесс.
    # Создать дочерний процесс в линуксе проще 
    # пареной репы – достаточно поместить номер сис.
    # вызова в EAX и сделать «а-ля int 21h», т.е. int 80h
      movl  $SYS_fork,%EAX
      int  $0x80  
    # все. 
    # Теперь у нас параллельно сосуществуют ДВА процесса:
    # родительский (в котором исполнялись все предыдущие 
    # команды) и дочерний. Что же содержит дочерний код? 
    # А все то же самое, что и родительский.
    # Т.е. важно понять, что # весь нижеследующий (и выше тоже)
    # код находиться в памяти в ДВУХ разных местах.
    # Как процессор переключается между 
    # ними (и всеми остальными живыми процессами) 
    # – читайте «Переключение задач» в интеловском мануале. 
    test  %EAX,%EAX  
    # вот эту команду необходимо осознать.
    # Прежде всего, важно понять, что данная команда
    # существует и в родительском 
    # и в дочернем процессах (об этом выше).
    # Следовательно выполниться она и там и там.
    # Все дело в том, что после 
    # int 80h родительскому процессу вернется PID сына
    # (в EAX ессесно, вообще все возвращается в EAX, как и в винде)
    # а что же вернется сыне? Правильно, нолик.
    # Именно поэтому следующий jmp будет выполнен
    # в дочернем процессе и 
    # не будет выполнен в родительском.

    # ребенок улетает на метку _cont1
      jz  _cont1   
    # ...а в это время, в родительском процессе:
      xorl  %EBX,%EBX  # EBX=status code
      xorl  %EAX,%EAX  #
      incl  %EAX   # SYS_exit 
    # завершаем родительский процесс.
      int  $0x80   

    # Теперь все дети 
    # управляются процессом INIT


    _cont1:
      movl  $SYS_setsid,%EAX
    # сделаем нашего ребенка главным в группе
      int  $0x80   
      
      movl  $1,%EBX   # SIGHUP
      movl  $1,%ECX   # SIG_IGN
      movl  $SYS_signal,%EAX
    # далее сигнал SIGHUP будет игнорироваться
      int  $0x80   
           
      movl  $SYS_fork,%EAX
    # наш ребенок уже подрос и теперь сам может родить сына
      int  $0x80   
    # (по сути – это уже внук нашему изначальному 
    # родительскому процессу)

    # EAX=0 в дочернем и EAX=PIDдочернего в родительском

      test  %EAX,%EAX  

    jz  _cont2   

    # внук нашего родительского (которого уже давно нет в 
    # живых) улетает на метку _cont2, однако отец все еще 
    # жив!!! (все как в мексиканском сериале)
      
      xorl  %EBX,%EBX  # EBX=status code
      xorl  %EAX,%EAX  #
      incl  %EAX   # SYS_exit
      int         $0x80   

    # вот уже и отец отправлен к деду на небеса (да, 
    # злостная программка, недаром демоном зовется)

    # ..а в это время внучок получает все наследство и 

    _cont2:

    # продолжает жить
    # далее, после того,
    # как все кровавые разборки и отцеубийства благополучно завершены,
    # внучок,продавший душу демону, 
    # преспокойно создает сокет.
    # Дело в том, что в линуксе есть такие системные вызовы,
    # для вызова которых их номер не 
    # помещается в EAX.
    # Вместо этого в EAX помещается номер функции-мультиплексора,
    # реализующий вызов конкретной 
    # функции номер которой помещается в EBX.
    # Так, например, происходит при вызове IPC и SOCKET-функций.
    # Кроме того, 
    # при вызове SOCKET-функций параметры располагаются не в регистрах,
    # а в стеке. Смотри как все просто:

      pushl  $0   # протокол
      pushl  $SOCK_STREAM  # тип

      pushl  $AF_INET   # домен
    # ECX должен указывать на кадр в стеке, содержащий 
      movl  %ESP,%ECX  
    # параметры, такая уж у него судьба...
      movl  $SYS_SOCKET,%EBX  
    # а вот это уже номер той самой конкретной функции
    # SOCKET – создать сокет
           
      movl  $SYS_socketcall,%EAX
     
    # в EAX - номер функции мультиплексора (по сути он 
    # просто перенаправит вызов в функцию, указанную в EBX
      
           int         $0x80

    # сокет создан! Ура товарищи. В EAX возвратиться его дескриптор.

    # «очистим» стек (по сути это выражение придумано специально
    # для HL-программистов, на самом деле ничего не 
    # очищается, данную операцию необходимо производить только
    # для того чтобы в дальнейшем не произошло переполнение 
    # стека, но в таких маленьких программках это делать вовсе
    # не обязательно):

      addl  $0xC,%ESP

      movl  %EAX,(sockfd)  

    # сохраним дескриптор созданного сокета в переменной
    # sockfd

    # далее необходимо осуществить операцию BIND,
    # которая называется «привязка имени сокету»,
    # хотя суть этого названия 
    # слабо отражает смысл происходящего на самом деле.
    # На самом деле BIND просто назначает конкретному сокету IP-
    # адрес и порт, через который с ним можно взаимодействовать:

    # размер передаваемой структуры (вообще подобран 
      pushl  $0x10
    # методом тыка, потому что логически непонятно почему 
    # именно 16)

    # указатель на структуру sockaddr_in

           pushl  $sockaddr_in
    # дескриптор нашего сокета
      pushl  %EAX   
    # ECX указывает на параметры в стеке
      movl  %ESP,%ECX  
    # номер функции BIND – в EBX
      movl  $SYS_BIND,%EBX
    # функция-мультиплексор
      movl  $SYS_socketcall,%EAX
      int  $0x80
    # теперь сокет «привязан» к конкретному IP-шнику и порту
    # поднимем ESP на место
      addl  $0xC,%ESP  
    # далее что-либо подробно описывать я не вижу смысла,
    # любой желающий сам без труда разберется, опираясь на 
    # полученные выше знания.
      pushl  $0   # backlog
      movl  (sockfd),%EAX
      pushl  %EAX
      movl  %ESP,%ECX
      movl  $SYS_LISTEN,%EBX
      movl  $SYS_socketcall,%EAX
      int         $0x80
      addl  $0x8,%ESP

    _wait_next_client:
      pushl  $0   # addrlen
      pushl  $0   # cliaddr
      movl  (sockfd),%EAX
      pushl  %EAX   # sockfd
      movl  %ESP,%ECX
      movl  $SYS_ACCEPT,%EBX
      movl  $SYS_socketcall,%EAX
      int  $0x80
      addl  $0xC,%ESP

      movl  %EAX,(connfd)
      
      movl  $SYS_fork,%EAX
      int  $0x80   # create child process
      test  %EAX,%EAX
      jnz  _wait_next_client

    _next_plain_text:
      movl  (connfd),%EBX
      movl  $buf,%ECX  # ECX->buf
      movl  $1024,%EDX  # 1024 bytes
      movl  $SYS_read,%EAX
      int  $0x80   # wait plain_text

      movl  $buf,%ESI
      movl  %ESI,%EDI
      movl  %EAX,%ECX
      movl  %EAX,%EDX
    _encrypt:
      lodsb
      cmp  $0x41,%AL  # A
      jb  _next
      cmp  $0x5A,%AL  # Z
      ja  _maybe_small
      incb  %AL
      incb  %AL   # encryption ;)
      cmp  $0x5A,%AL
      jle  _next
      sub  $26,%AL
    _maybe_small:
      cmp  $0x61,%AL  # a
      jb  _next
      cmp  $0x7A,%AL  # z
      ja  _next
      incb  %AL
      incb  %AL   # encryption ;)
      cmp  $0x7A,%AL
      jle  _next
      sub  $26,%AL
    _next:
      stosb
      loop  _encrypt
      
      movl  (connfd),%EBX
      movl  $buf,%ECX  # ECX->chiper_text
      movl  $SYS_write,%EAX
      int  $0x80   # send plain_text
      
      jmp  _next_plain_text
    # *****************************************************
      .data
    sockfd:  .long  0
    connfd:  .long  0
    sockaddr_in: 
    sin_family: .word  AF_INET
    sin_port: .word  0x3930   # port:12345
    sin_addr: .long  0   # INADDR_ANY
    buf:
    # *****************************************************



    #Клиент пишется по аналогии с сервером,
    # думаю сами без труда разберетесь:


    # *********************************************************
    # Client
    # by Broken Sword [HI-TECH]
    # (for Linux based on Intel x86 only)

    # brokensword@mail.ru
    # www.wasm.ru

    # Compile Instructions:
    # ---------------------------------------------------------
    # as client.s
    # ld --strip-all -o client a.out
    # *********************************************************

    # *********************************************************  
           .include  "syscalls.inc"
      .include  "def.inc"
      .text
      .globl  _start
    _start:
      pushl  $0   # protocol
      pushl  $SOCK_STREAM  # type
      pushl  $AF_INET   # domain
      movl  %ESP,%ECX
      movl  $SYS_SOCKET,%EBX
      movl  $SYS_socketcall,%EAX
      int  $0x80
      addl  $0xC,%ESP

      movl  %EAX,(sockfd)

      pushl  $0x10   # addrlen
      pushl  $sockaddr_in
      pushl  %EAX   # sockfd
      movl  %ESP,%ECX
      movl  $SYS_CONNECT,%EBX
      movl  $SYS_socketcall,%EAX
      int  $0x80
      addl  $0xC,%ESP

    _next_plain_text:
      xorl  %EBX,%EBX  # stdin
      movl  $buf,%ECX  # ECX->buf
      movl  $1024,%EDX  # 1024 bytes
      movl  $SYS_read,%EAX
      int  $0x80   # read from stdin

      movl  (sockfd),%EBX
      movl  $buf,%ECX  # ECX->plain_text
      movl  %EAX,%EDX  # bytes read
      movl  $SYS_write,%EAX
      int  $0x80   # send plain_text

      movl  $SYS_read,%EAX
      int  $0x80   # wait chiper_text
      
      xorl  %EBX,%EBX
      incl  %EBX   # EBX=1 (stdout)
      movl  $SYS_write,%EAX
      int  $0x80   # disp chiper_text
      
      jmp  _next_plain_text
    # *********************************************************
      .data
    sockfd:  .long  0
    sockaddr_in: 
    sin_family: .word  AF_INET
    sin_port: .word  0x3930   # port:12345
    sin_addr: .long  0x0100007F  # 127.0.0.1
    buf:
    # *********************************************************

Вот так вот все просто (вся сложность на самом деле заключена в понимании стека TCP/IP, а не в том, как закодировать все эти действия на асме).

Все - запускайте бесов server, а за ним client.

p.s. использовать данное приложение для шифрования важных данных на диске не рекомендуется ).

Благодарности (в алфавитном порядке :)):

Aquila [HI-TECH]
CyberManiac [HI-TECH]
Edmond [HI-TECH]
Vladimir [HI-TECH]

...и всей группе HI-TECH! Держитесь, ребята!

Broken Sword [HI-TECH]

# Эпилог