hello world машинный код

Изучаем C++. Часть 1. Что такое программа и языки программирования

Разбираемся, как устроен язык программирования C++, что такое программы и как научиться их писать.

Чтобы научиться программировать с нуля, нужно только уметь пользоваться компьютером, устанавливать программы, создавать папки и файлы. Это первая статья из серии «Глубокое погружение в C++», с помощью которой вы сделаете первые шаги в профессии разработчика серверных приложений и игр.

Не пугайтесь большого объёма информации: чем больше вы будете знать, тем лучше сможете программировать. Это особенно важно, если вы выбрали C++, потому что на этом языке невозможно без хорошей теоретической базы написать высокопроизводительный код.

Внимание! В следующих разделах информация сильно упрощена.

hello world машинный код

Пишет о программировании, в свободное время создает игры. Мечтает открыть свою студию и выпускать ламповые RPG.

Что такое язык программирования

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

Компьютер понимает только машинный код — специальный язык, который очень сложно изучить. Вот как выглядит вывод на экран надписи «Hello, World!» на машинном коде (в шестнадцатеричном представлении):

Чтобы упростить разработку, были созданы языки программирования — с их помощью пишут инструкции для компьютера, которые понятны и человеку. Одним из первых был язык ассемблера. Вот как на нём выглядит вывод надписи «Hello, World!»:

hello world машинный код

Всё равно достаточно сложно, да? В результате человечество создало более понятные языки программирования, на которых гораздо быстрее писать программы. А чтобы они потом запускались, код переводится в машинный или какой-нибудь промежуточный язык — этот процесс называется компиляцией.

Если язык похож на машинный код, то его называют низкоуровневым. Если же он больше понятен человеку, то — высокоуровневым. C++ сочетает в себе свойства и того, и другого.

Как работают программы

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

Например, браузер получает текст из адресной строки, который ввёл пользователь, отправляет адрес на сервер, а потом выводит ответ в виде страницы.

Также программа может состоять из других подпрограмм — наборов инструкций. Обработка запроса пользователя, отправка данных на сервер, получение ответа, вывод страницы — всё это подпрограммы.

Вот пример логики такой подпрограммы:

hello world машинный код

Можно вернуть данные пользователю, записать их в файл или передать другой подпрограмме. Вот ещё один пример использования программы:

Эта команда запускает программу в ОС Linux, которая конвертирует изображение img.jpg в другой формат, а потом сохраняет в файл img.png.

Сейчас слово «подпрограмма» встречается редко — вместо него используют слова «функция», «процедура» или «метод».

Пишем первую программу на C++

По традиции программистов первая программа, написанная на изучаемом языке, должна выводить на экран надпись «Hello, World!».

hello world машинный код

В C++ такой код будет выглядеть так:

Попробуем его запустить, а потом разберём, как он работает.

Запускаем код в интернете

Самый простой вариант — зайти на сайт repl.it. На главной странице нажмите Start Coding, выберите C++ и нажмите Create Repl.

У вас откроется следующая страница:

hello world машинный код

Слева — менеджер файлов, в центре — текстовый редактор, где уже введён нужный нам код, а справа — консоль, в которой выводится результат. Чтобы запустить программу, нажмите Run.

Запускаем код на компьютере

Если же вы хотите компилировать код на своём компьютере, то установите компилятор. Я пользуюсь G++, его и вам рекомендую.

После установки компилятора создайте отдельную папку для кода, а в ней — файл hello.cpp (это расширение для файлов с командами на C++). Желательно, чтобы путь к папке состоял из латинских символов и был написан без пробелов.

Откройте hello.cpp с помощью любого блокнота или редактора кода (например, Sublime text или VS Code) и вставьте в него код, указанный выше. Теперь откройте терминал с помощью инструкции ниже.

WindowsLinux
1. Нажмите Win + R, введите cmd и нажмите Enter.1. Нажмите Ctrl + Alt + t. Если не помогло, то подходящий способ можно найти в этой статье.
2. Введите команду cd [путь]. Например, cd projects\cpp1, если ваша папка находится по адресу c:\projects\cpp1.2. Введите команду cd [путь]. Например: cd

/projects/cpp1

3. Если папка находится не на системном диске, то команду нужно изменить: cd /D [диск:][путь]. Например: cd /D d:\projects\cpp1

Затем введите следующую команду:

Эта команда скажет компьютеру, что нужно скомпилировать код из файла hello.cpp и сохранить результат в файл hello (или hello.exe в Windows).

После этого запустите скомпилированную программу, и вы увидите, что всё работает:

hello world машинный код

Теперь, когда мы смогли запустить программу, разберёмся в ней.

Из чего состоит программа на C++

Начнём с самой первой строчки:

hello world машинный код

С помощью языка программирования можно:

Но чтобы получить больше возможностей, программе нужно как-то взаимодействовать с операционной системой. Писать код, который будет отправлять команды ОС, достаточно сложно, но нам и не придётся — всё уже написано другими разработчиками и помещено в специальную библиотеку.

Одна из таких библиотек, iostream, позволяет запрашивать пользовательский ввод или выводить что-то в консоли.

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

Источник

Hello World из байт-кода для JVM

Скомпилируем простенькую программу выводящую «Hello World» и пройдемся по его структуре

Не думаю, что статья будет достаточно информативной для тех, кто поверхностно не знает как выглядит байт-код и как с ним работает JVM (например, хотя бы простейшие инструкции (знание об их существовании)).

На самом деле, это не так сложно. Достаточно использовать инструмент javap из JDK и рассмотреть дизассемблированный код.

А мы приступим к разбору самой структуры байт-кода для JVM

Очень полезной книгой для этого стала официальная спецификация JVM — The Java Virtual Machine Specification на сайте oracle

Для начала создадим простенькую программу:

Скомпилируем её командой javac Main.java и собственно сделаем дизассемблинг

Это просто представление байт-кода, которое человеку видеть легче, чем оригинальный байт-код, но сам он выглядит иначе:

С этим кодом мы и будем работать.

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

Её вы можете найти в спецификации JVM Chapter 4.1 The ClassFile Structure

Тут все просто — слева указана размерность в байтах, а справа описание.

Разбирать байт-код мы будем в hexadecimal, где каждая цифра занимает 4 бита, а следовательно, на два байта — 4 цифры и на четыре байта — 8 цифр.

magic

minor_version, major_version

Это версии вашего class файла. Если мы назовем major_version M и minor_version m, то получаем версию нашего class файла как M.m

Сейчас я сразу буду приводить примеры из нашей программы «Hello World», чтобы посмотреть как они используются:

Его же мы можем видеть в дизассемблированном коде, но уже в десятичной системе счисления:

constant_pool_count

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

Также следует не забывать, что вы должны писать туда количество_переменных_в_пуле + 1

constant_pool[]

Каждый тип переменной в пуле констант имеет свою структуру:

Таблица с тэгами можно найти в спецификации Table 4.3 Constant pool tags

Собственно, вот табличка:

Constant TypeValue
CONSTANT_Class7
CONSTANT_Fieldref9
CONSTANT_Methodref10
CONSTANT_InterfaceMethodref11
CONSTANT_String8
CONSTANT_Integer3
CONSTANT_Float4
CONSTANT_Long5
CONSTANT_Double6
CONSTANT_NameAndType12
CONSTANT_Utf81
CONSTANT_MethodHandle15
CONSTANT_MethodType16
CONSTANT_InvokeDynamic18

Как ранее уже говорилось, каждый тип константы имеет свою структуру.

Вот, например, структура CONSTANT_Class :

Структура поля и метода:

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

Рассмотрим часть нашего кода:

Итак, смотрим на структуру константы и узнаем, что первый байт отведен под тип константы. Здесь мы видим 0a (10) — а, следовательно, это CONSTANT_Methodref

Смотрим его структуру:

После одного байта для тэга, нам нужно еще 4 байта для class_index и name_and_type_index

Отлично, мы нашли одну из значений пула констант. Идем дальше. Смотрим, 09 — значит тип CONSTANT_Fieldref

Вам может показаться, что большинство типов имеет одинаковую форму, но это не так.
Например, структура следующего типа выглядит так, CONSTANT_String :

Все эти структуры можно посмотреть в Chapter 4.4 The Constant Pool

Теперь разберем, что значат типы внутри самого info

Это же мы можем видеть в дизассемблированном коде:

Также можно выделить представление чисел и строк.

Про представление чисел можно прочитать начиная с главы 4.4.4, а мы пока разберем лишь строки, так как числа пока не входят в программу Hello World

Собственно, вот так представляется строка:

Например, наш Hello World:

Разбирая весь пул констант байт-кода, получим:

Также, мы можем сравнить его с дизассемблированным кодом:

Тем самым проверив, что все совпадает, ведь по сути javap просто обрабатывает этот байт-код и показывает нам его в форматированном виде.

Пул констант нужен для инструкций. Например:

Подробнее обо всех типах в пуле констант можно узнать в Chapter 4.4 The Constant Pool

Идем дальше по структуре ClassFile

access_flags

Это битовая маска для свойств модификаторов

Flag NameValueInterpretation
ACC_PUBLIC0x0001Declared public ; may be accessed from outside its package.
ACC_FINAL0x0010Declared final ; no subclasses allowed.
ACC_SUPER0x0020Treat superclass methods specially when invoked by the invokespecial instruction.
ACC_INTERFACE0x0200Is an interface, not a class.
ACC_ABSTRACT0x0400Declared abstract ; must not be instantiated.
ACC_SYNTHETIC0x1000Declared synthetic; not present in the source code.
ACC_ANNOTATION0x2000Declared as an annotation type.
ACC_ENUM0x4000Declared as an enum type.

this_class

Должна содержать адрес на this класса. В нашем случае, она находится по адресу 5:

Следует заметить, что структуру этой переменной должна соответствовать CONSTANT_Class_info

super_class

То есть в этих ячейках указан name_index из структуры:

interfaces_count, fields_count

methods_count

Количество методов. Хоть и в коде мы видим один метод в классе, но, на самом деле, их два. Кроме main метода еще есть конструктор по умолчанию. Поэтому их количество равно двум, в нашем случае.

methods[]

Каждый элемент должен соответствовать структуре method_info описанной в Chapter 4.6 Methods

В нашем байт-коде (отформатированном, с комментариями) выглядит это так:

Разберем по-подробнее структуру методов:

access_flags

К слову, ACC_VARARGS здесь необязательный, в том плане, что, если бы мы
использовали String[] args вместо String… args, то этого флага бы не было

name_index

descriptor_index

Грубо говоря, это адрес указывающий на дескриптор метода. Этот дескриптор содержит тип возвращаемого значения и тип его сигнатуры.

Также, в JVM используются интерпретируемые сокращения:

BaseType CharacterTypeInterpretation
Bbytesigned byte
CcharUnicode character code point in the Basic Multilingual Plane, encoded with UTF-16
Ddoubledouble-precision floating-point value
Ffloatsingle-precision floating-point value
Iintinteger
Jlonglong integer
L ClassName ;referencean instance of class ClassName
Sshortsigned short
Zbooleantrue or false
[referenceone array dimension

В общем случае это выглядит так:

Например, следующий метод:

Можно представить в виде

Далее, идут атрибуты, которые также имеют свою структуру.

Но сначала, как и всегда, идет его количество attributes_count

Затем сами атрибуты со структурой описанной в Chapter 4.7 Attributes

attribute_name_index

attribute_length

Содержит длину атрибута, не включая attribute_name_index и attribute_length

info

max_stack

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

Упрощенно говоря, JVM выделит место для стека операндов. Там можно указывать значение, которое больше, чем нужно, но определение в этом атрибуте значения меньше, чем нужно приведет к ошибке.

max_locals

Максимальный размер локальных переменных

Ознакомится с локальными переменными можно либо в Mastering Java Bytecode at the Core of the JVM или в том же JVM Internals

code_length

Размер кода, который будет исполнятся внутри метода

code[]

Каждый код указывает на какую-то инструкцию. Таблицу соотношения optcode и команды с мнемоникой можно найти в википедии — Java bytecode instruction listings или в самой спецификации в конце книги

Для примера, возьмем наш конструктор:

Здесь мы можем найти наш код:

Ищем в таблице команды и сопоставляем:

Также описания этих команд можно найти здесь: Chapter 4.10.1.9. Type Checking Instructions

exception_table_length

Задает число элементов в таблице exception_table. У нас пока нет перехватов исключений поэтому разбирать его не будем. Но дополнительно можно почитать Chapter 4.7.3 The Code Attribute

exception_table[]

Имеет вот такую структуру:

attributes_count

Количество атрибутов в Code

attributes[]

Атрибуты, часто используются анализаторами или отладчиками.

Средства для работы с байт-кодом

Это немного не та тема, которая относится к данной статье, но все же косвенно связанная с ней.

Средств для работы с байт-кодом, на самом деле, достаточно много. Здесь я бы хотел рассмотреть Byte Code Engineering Library (BCEL) от Apache Commons.

Для начала, с помощью него мы может получить некоторые атрибуты байт-кода:

Помимо этого мы можем сгенерировать, изменить или дизассемблировать (например, в Jasmin) байт-код.

Парочку примеров можно найти в моем репозитории или в официальных примерах

Также, я уделил внимание и Jasmin. На самом деле, я не знаю, чем оно может быть полезно, но я её использовал при изучении механизма работы JVM с байт-кодом.

С помощью неё можно писать на упрощенном ассемблерном коде:

Вот мы и разобрали простую программку Hello World

Листинг байт-кода с комментариями можно найти на моем гисте: gist.github

Если есть ошибки прошу писать в комментариях или в сообщениях.

Источник

Hello world машинный код

Многие любители не испытывают серьезных трудностей в овладении БЕЙСИКом. Для этого достаточно немного практики. Но рано или поздно они приходят к барьеру «машинного кода». Как это ни печально, но некоторые так перед ним и останавливаются. Это ни в коей мере не связано с отсутствием желания или способностей, просто многие не знают, с чего начать. Если в БЕЙСИКе можно начинать с чего угодно (при ошибке компьютер сам Вас поправит), то здесь Вы оказываетесь с процессором один на один, и такой метод проб и ошибок не срабатывает.

Итак, давайте напишем первую программу в машинном коде. Прежде всего, выделим для нее область памяти. Если Вы читали нашу книгу «Большие возможности Вашего «ZX-Spectrum`а», то знаете, что для БЕЙСИКа в оперативной памяти компьютера отведена область памяти, начинающаяся с адреса, на который указывает системная переменная PROG и заканчивается адресом, на который указывает системная переменная RAMTOP. Предположим, что Вы хотите записать программу в машинных кодах, начиная с адреса 30000. Дайте команду CLEAR 29999. Эта команда установит RAMTOP в 29999 и Ваша программа будет защищена от возможной порчи из БЕЙСИКа. Даже если Вы дадите команду NEW, области памяти, находящиеся выше RAMTOP, не будут поражены.

Теперь дайте две прямые команды одну за другой:

Если все, что Вы здесь прочитали, Вам понятно, то Вы уже поняли, как составляются программы в машинных кодах. Можно, конечно, возразить, что пользы от такой программы не очень много, но сейчас не в этом суть. Важно, чтобы Вы поняли, что некая последовательность чисел может быть последовательностью команд для процессора Z-80.

Теперь давайте вернемся к нашей первой программе и попробуем ее несколько развить, чтобы она все же что-то делала. Процессор Z-80 имеет несколько регистров, у которых есть имена – «А», «В», «С» и т.д. Каждый из них может содержать одно какое-либо целое число от 0 до 255 (т.е. один байт).

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

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

Источник

Java байткод «Hello world»

На хабре уже есть статья про java байткод. Я решил ее немного дополнить и в меру сил развить тему. Мне кажется довольно логичным разобрать простейшее приложение на Java. А что может быть проще «Hello world»?
Для своего эксперимента я создал директорию src, куда в папку hello положил файл App.java:

Скопилируем файл командой:

На выходе в папке classes у меня появился файл App.class Для начала сравним размеры java и class файлов.

App.java 139B
App.class 418B

Это было неожиданно. Мне почему-то казалось, что скомпилированный файл должен быть меньше. Попытаюсь открыть class файл:

Довольно непривычный вид для Java кода. Попробуем с помощью описание формата class файлов понять, что здесь закодировано.

Это 4 байта для magic, который определяет формат файла.

minor version — Минорная версия как следует из названия

major version — 2 байта под мажорную версию.
Сочетание minor и major version говорит о том, что я компилировал этот код с помощью J2SE 8.

Эти два байта представляют constant_pool_count и отвечают за размер constant_pool. В моем случае count равен 29, а размер пула, соответственно 28. Дальше идут элементы вида:

cp_info <
u1 tag; // 1 байт на тег
u1 info[]; // массив с описанием
>

Рассмотрим элементы в constant_pool.

Этот тег соответствует CONSTANT_Methodref, а значит дальше должно быть описание:

CONSTANT_Methodref_info <
u1 tag;
u2 class_index;
u2 name_and_type_index;
>
соответственно:

class_index, указывает на 6 элемент в constant_pool

name_and_type_index, указывает на 15 элемент в constant_pool

Пока не понятно, на какой метод указывает эта ссылка и мы идем дальше:

Это CONSTANT_Fieldref, а значит дальше ищем:

CONSTANT_Fieldref_info <
u1 tag;
u2 class_index;
u2 name_and_type_index;
>

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

class_index в 16 элементе

name_and_type_index в 17 элементе

tag для CONSTANT_String

получаем, что самое интересное лежит в 18 элементе:

Tag соответствующий ссылке на метод:
класс которого описан в 19 элементе

a название и тип в 20 элементе:

5-ый элемент:
Tag для CONSTANT_Class

название, которого в 21 элементе

6-ой элемент:
Cнова CONSTANT_Class

c названием в 22 элементе

Как мы помним 1-ый элемент constant_pool относится к этому классу.

7-ой элемент:
tag, CONSTANT_Utf8, первая строчка

Она должна соответствовать:

CONSTANT_Utf8_info <
u1 tag;
u2 length;
u1 bytes[length];
>

Тогда длина нашей строчки 6 байт:

Это особое название, так помечаются конструкторы.

строчка длины 3 — «()V»:

Это описание нашего конструктора без параметров, который был упомянут в седьмом элементе.

9-ый элемент:
CONSTANT_Utf8

10-ый элемент:
Строка LineNumberTable

15-ый элемент
Tag, соответствует CONSTANT_NameAndType

а значит нам понадобится

CONSTANT_NameAndType_info <
u1 tag;
u2 name_index;
u2 descriptor_index;
>

и тогда:
ссылка на 7 элемент

ccылка на 8 элемент

Учитывая что первый элемент ссылался на это, мы можем заключить что первым был объявлен конструктор класса без параметров. Название класса, мы должны найти в 22 элементе.

16-ый элемент:
Tag, для CONSTANT_Class

c названием в 23 элементе

17-ый элемент:
Tag, CONSTANT_NameAndType, со ссылкой на 24 и 25 элемент constant_pool

18-ый элемент:
Ура «Hello world!»

19-ый элемент:
Tag, для CONSTANT_class c названием в 25-ом элементе

20-ый элемент:
Tag CONSTANT_NameAndType cо ссылкой на 27 и 28 элемент

25-ый элемент:
«Ljava/io/PrintStream;»

26-ой элемент:
«java/io/PrintStream»

methods_count у нас 2 метода в классе, конструктор по умолчанию и метод main:

Method 1 — Constructor

Один из самых интересных аттрибутов с кодом нашего метода code[code_length], разбор инструкций отдельная большая тема:

Аттрибут закончился и продолжается описание метода

Attribute 1 код метода main

Описание методов закончено и идет описание атрибутов класса

Теперь когда мы закончили с по-байтовым разбором class файла, становится понятно как работает:

Он автоматически выводит тоже самое, что я выписал руками:

А вот здесь можно посмотреть пример разбора class файла:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *