использование с++
Есть несколько вопросов по этому поводу:
1) в каком формате компилировать сишную программу
2) как найти точку входа в неё
3) как из программы на С вызвать ассемблерную функцию
4) как из программы на С запустить прерывание
Помогите, пожалуйста.
0) RTFM в больших количествах в любое время суток до и после еды
1) компилируй в каком хочешь - хоть PE, хоть ELF, хоть сырой бинарник. и бутсектор пиши для загрузки определенного формата.
2) сначала загрузить. потом уже по загруженному адресу передавать управление. естественно, все ещё зависит от выбранного формата. проще всего с сырым бинарником
3) зависит от компилятора. обычно нечто вроде asm("функция");
4) из вставленного кода на ассемблере
С++ не советую тут брать - С больше подходит.
З.Ы. прочитав то, что я тут понаписал, идешь читать документацию, если что-то непонятно. и не спешишь писать "А ШТО ТАКОИ ... ???"
1) компилируй в каком хочешь - хоть PE, хоть ELF, хоть сырой бинарник. и бутсектор пиши для загрузки определенного формата.
Что надо сказать компилятору (допустим gcc), чтобы он сделал сырой бинарник?
2) сначала загрузить. потом уже по загруженному адресу передавать управление. естественно, все ещё зависит от выбранного формата.
проще всего с сырым бинарником
И где у него точка входа? Первой командой?
3) зависит от компилятора. обычно нечто вроде asm("функция");
Я имел ввиду, что где-то в памяти есть прога на ассемблере и я знаю её адрес. Как в таком случае запустить её из С?
С++ не советую тут брать - С больше подходит.
Ну пусть будет С
Что надо сказать компилятору (допустим gcc), чтобы он сделал сырой бинарник?
слушай, те про RTFM намек не ясен? man gcc трудно почитать?
я сырой бинарник делаю:
ld --oformat binary
И где у него точка входа? Первой командой?
читай внимательно - все зависит от выбранного формата. берешь, читаешь. у сырого обычно первой командой. или логика хромает?
Я имел ввиду, что где-то в памяти есть прога на ассемблере и я знаю её адрес. Как в таком случае запустить её из С?
во первых, пиши определеннее, в памяти нет загруженых программ на ассемблере, есть только исполняемый код. без разницы, на чем написанный.
во-вторых, учим основы Си - указатели на функцию:
start();
я сырой бинарник делаю:
ld --oformat binary
При попытке так откомпилировать
вылезает нечто на пять килобайт.
сомневаюсь, что это то что нужно.
вылезает нечто на пять килобайт.
вообще неплохо сказать, что пишут, хотя я кажется понял в чем соль:
эт кажется я забыл флаг приписать
ld --oformat binary
gcc:
-ffreestanding - запрет использования станд. библиотек
-c - выдать объектный файл, а не бинарник (!)
ld:
--oformat binary - скомпоновать сырой бинарник
ели не получится, приведи команды, которыми ты это все собираешь полностью и приведи результат их выполнения.
asmfunc();
}
asmPart.s
asmfunc:
movb $0, %bh
movb $'s', %al
movw $10, %cx
movb $0xa, %ah
int $0x10
ret
# gcc -o asmPart.o -ffreestanding -c asmPart.s
# ld -o prog --oformat binary cPart.o asmpart.o
ld: warning: cannot find entry symbol _start; defaulting to 08048000
# ls -gG
итого 48
-rw-rw-r-- 1 481 Ноя 18 19:14 asmpart.o
-rw-rw-r-- 1 481 Ноя 18 19:21 asmPart.o
-rw-rw-r-- 1 100 Ноя 18 19:19 asmPart.s
-rw-rw-r-- 1 27 Ноя 18 18:42 cPart.c
-rw-rw-r-- 1 784 Ноя 18 19:21 cPart.o
-rwxrwxr-x 1 49 Ноя 18 19:22 prog
Загружаю prog по адресу 0x2000:0x0000,
и по нему же передаю управление : call 0x2000:0x0000
По идее программа должна вывести "ssssssssss" в текущей позиции курсора, но не делает этого.
Кроме того она не возвращает управление вызвавшей программе.
.text
.globl start, _start
start:
_start:
jmp multiboot_entry
.align 4
multiboot_entry:
pushl $0
popf
xor %eax, %eax
xor %ebx, %ebx
xor %edx, %edx
xor %ecx, %ecx
movb $0x10, %al //сюда свой селектор
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
movl $STACK_ADDR, %esp
call EXT_C(main)
loop: hlt
jmp loop
startup.h:
#define startup_h
#ifdef HAVE_ASM_USCORE
# define EXT_C(sym) _ ## sym
#else
# define EXT_C(sym) sym
#endif
#endif
( вообще, как я говорил, ман рулит. почитай man multiboot )
компиляешь все это дело так:
# ld -o prog --oformat binary startup.o cPart.o asmpart.o
Ещё проблема - собирается по умолчанию 32-битный код, насчет 16 битного - кури мануал
Теперь работает.
Зачем нужен код, который ты написал, не понимаю:
ну очищает он регистры - какой от этого смысл?
Осталась одна проблема:
что делать, если вместо бесконечного цикла
loop: hlt
jmp loop
после выполнения программы надо вернуть управление обратно в загрузчик?
"ret" вроде бы ассемблируется как возврат после ближнего перехода. В этом случае он не работает.
Зачем нужен код, который ты написал, не понимаю:
ну очищает он регистры - какой от этого смысл?
вообще он нужен для перехода на функцию main, ибо до нее обычно лежат строковые данные.
насчет ret посмотрю.
P.S Про ret я сам нашёл - надо lret использовать.
cPart.c
int main() {
writeNumber(143);
nextLine();
}
asmPart.s (и здесь же startup)
.globl _start
_start:
call main
lret
.globl nextLine
nextLine:
... (переводит курсор на следущую строчку)
ret
.globl writeNumber
writeNumber:
movl 4(%esp), %ebx
... Этот код выводит на экран число,
... которое находится в %bx.
... Он совершенно точно работает правильно.
ret
По идее этот код должен вывести на экран "143", но выводит "0" :(
Может быть я неправильно извлекаю параметер из стека?
может я и ошибаюсь, но кажись сначала идет адрес возврата, потом уже параметр. поэтому если у тебя код 16-битный, сначала в стеке будет 2 байта адреса возврата. непонятно только, как будет интерпретироваться параметр. я бы посоветовал попробовать попробовать 6 байт
З.Ы., а как таки в 16-битном коде ты юзаешь 32-битные регистры? если действительно проходит, то если не получится
попробуй уж и
Я считал что адрес возврата - 4 байта, а он 2.
Вот так заработало:
movl 2(%esp), %ebx (а не 6)
32-битные регистры почему-то всё-таки работают
(gcc, кстати, независимо от ".code16" использует 32-битные)
Теперь попробовал сделать более сложный вариант:
int main() {
for (int a=0;a<2;a++) {
writeNumber(a);
nextLine();
}
}
gcc возмущается -
cCode.c: In function ‘main’:
cCode.c:4: error: ‘for’ loop initial declaration used outside C99 mode
не понимаю что это значит
вот так компилируется, но не работает:
int main() {
int a;
for (a=0;a<2;a++) {
writeNumber(a);
nextLine();
}
}
выводит
0
0
0
0
0
(и.т.д на весь экран)
~4 байта 2 байта
в реальном режме 32-битных регистров просто не должно быть...
-----------------------
эт он ругаеццо на объявление переменной внутри цикла, не по сишному стандарту это
попробуй a объявить с типом char или short
как-то странно, почему
~4 байта 2 байта
Ну почему странно?
стек будет выглядеть так:
[адрес возврата] [параметер]
xxxxxxxx xxxxxxxx 000 000 000 143
^
esp сюда.
тогда esp+2 указывает на первый байт параметра, что от него и требуется.
в реальном режме 32-битных регистров просто не должно быть...
Может тогда из-за этого и не работает... (хотя врядли).
А запретить gcc использовать 32-битные регистры я не могу...
эт он ругаеццо на объявление переменной внутри цикла, не по сишному стандарту это
попробуй a объявить с типом char или short
Тот же результат.
А когда я выношу объявление за пределы цикла компилируется, но не работат: зацикливается и выводит много нулей.
стек будет выглядеть так:
[адрес возврата] [параметер]
xxxxxxxx xxxxxxxx 000 000 000 143
^
esp сюда.
блин, это я дундук, с недосыпу не туда вершину стека мысленно прилепил :o прошу прощения
А когда я выношу объявление за пределы цикла компилируется, но не работат: зацикливается и выводит много нулей.
таки что-то со стеком косо, попробуй объявить глобально (за пределами main)
можешь ещё выдать команду
выдаст тебе ассемблерный листинг, посмотри, как в нем будут локальные переменные обрабатываться.
таки что-то со стеком косо, попробуй объявить глобально (за пределами main)
пробовал - не работает
gcc -S пробовал сделать - вроде там всё правильно, единственное, что плохо - он использует исключительно 32-битные регистры
Либо найти способ (или компилатор) для компиляции Си в 16-битный код, но по-моему gcc этого делать не умеет
Появилась новая проблема:
gcc при компиляции делит всё на несколько секций, и в каждой из них адреса отсчитываются от начала этой секции.
ld похоже просто помещает эти секции одну за другой в бинарнике.
когда я из boot-sectora это дело запускаю, нужно настроить ds на начало секции данных. Непонятно как это сделать - кто его знает, на какой адрес эти данные попали... кроме того этот адрес нужно знать ещё до компиляции...
я вижу два пути решения проблемы:
1) пытаться сделать, чтобы при компиляции всё пихалось в одну секцию
2) сделать, чтобы ld поместил секцию данных по заданному адресу в бинарнике.
наверное и то и другое можно сделать указав какую нибудь опцию ld,
но сколько я не смотрел man ничего подходящего не нашёл.
Посоветуйте что нибудь.
Или я неудачно вопрос задал?
google тоже не помогла...
http://www.gnu.org/software/binutils/
А там есть разделы "Documentation"
Очень сложно найти, практически невозможно, не так ли?