Вызов функции C main() из 32-битной сборки x86 _start

Я пытаюсь написать домашнее задание, которое заключается в следующем:

напишите простую программу на ассемблере, которая все, что она делает, это вызывает программу на C и отправляет ей аргументы командной строки, чтобы она могла правильно работать с (argc и argv).

Как это может быть сделано? Этот asm нам дали в рамках задания:

section .text

  global _start
  extern main

_start:

  ;;code to setup argc and argv for C program's main()

  call    main

  mov eax,1
  int 0x80

Итак, я хочу знать, где находятся argc и argv? Кроме того, мне просто нужно поместить указатель на argc в регистр eax, как при возврате значения в обычную функцию C, а программа C сделает все остальное?

В конце концов, после компиляции моей программы на C со следующим Makefile (как я уже сказал, я новичок в сборке, и это Makefile, данный нам учителем, я не до конца его понимаю):

%.o: %.asm
        nasm -g -O1 -f elf -o $@ $<

%.o: %.c
        gcc -m32 -g -nostdlib -fno-stack-protector -c -o $@ $<

all: lwca

lwca: lwc.o start.o
        ld  -melf_i386 -o $@ $^

Запуск ./lwca arg1 arg2 должен привести к argc = 3 и argv[1]=arg1 argc[2]=arg2

ОТВЕТ: Отсутствие ответа полностью решило мою проблему, в конце концов сработало следующее:

pop    dword ecx    ; ecx = argc
mov    ebx,esp      ; ebx = argv
push   ebx   ; char** argv
push   ecx   ; int argc


call    main

person Nivolas    schedule 24.11.2017    source источник
comment
выполнение программы и передача аргументов выполняются на уровне операционной системы. Вы можете использовать call для функции из скомпилированной библиотеки C, но не для запуска и выполнения другой программы.   -  person Jacek    schedule 24.11.2017
comment
Какие значения вы собираетесь предоставлять в качестве argc (количество строк командной строки) и argv (массив указателей на char, представляющий строки командной строки)? У вас есть такие? Хотите просто заявить, что их нет? Пожалуйста, объясните среду, это выглядит как код запуска для встроенного устройства, которое должно быть связано с вектором сброса. Существует ли что-нибудь вроде командной строки, которая запускает это?   -  person Yunnosch    schedule 24.11.2017
comment
@Rob: stackoverflow.com/questions/8863042/ не является дубликатом. Этот вопрос хочет реализовать минимальную замену кода запуска CRT, который обычно вызывает main. Тот, на который вы ссылаетесь, пишет main на ассемблере и вызывает другие функции C.   -  person Peter Cordes    schedule 24.11.2017
comment
В вашем заголовке указано x64 (т.е. x86-64), но тогда ваш вопрос заключается в том, чтобы создать его как 32-битный исполняемый файл. Название неправильное?   -  person Peter Cordes    schedule 24.11.2017
comment
Почему вы используете nasm -O1 (минимальную оптимизацию) вместо многопроходной оптимизации по умолчанию?   -  person Peter Cordes    schedule 24.11.2017
comment
Кстати, я бы посоветовал вам скомпилировать Hello World в обычном режиме на C, затем gdb ./hello и установить точку останова на _start и выполнить пошаговый код запуска gcc. Он выполняет значительно больше работы, чем вы ожидаете, в основном проверяя списки конструкторов, чтобы увидеть, нужно ли что-то запускать при запуске перед вызовом main. (В простом приветственном мире они будут пустыми.) main() — это обычная функция, вызываемая обычным способом (для 32-разрядных систем с аргументами в стеке).   -  person Peter Cordes    schedule 24.11.2017
comment
@Yunnosch: По int 0x80 мы можем сказать, что это Linux. i386 System V ABI говорит, что argc указывает указатель стека при запуске процесса (т. е. при входе в _start), а argv начинается на одну запись выше этого. (массив по значению, а не указатель на него).   -  person Peter Cordes    schedule 24.11.2017
comment
@Nivolas: Как на самом деле была сформулирована твоя домашняя работа? Он действительно сказал вызвать другую программу? Поскольку это подразумевает, что вы должны выполнять системный вызов execve для отдельно скомпилированного исполняемого файла, а не заменять/повторно реализовывать код запуска CRT, который вызывает main.   -  person Peter Cordes    schedule 24.11.2017
comment
@PeterCordes, значит, это означает, что указатель на argc находится в esp и что argv начинается с esp+4? Домашнее задание было сформулировано так, как я написал, код сборки, который я ввел, также был передан мне.   -  person Nivolas    schedule 24.11.2017
comment
Да, в 32-битном исполняемом файле. См. документацию по ABI: github.com/hjl-tools /x86-psABI/wiki/intel386-psABI-1.1.pdf, который описывает состояние запуска процесса. Это то же самое, что и в связанном дубликате, за исключением того, что для x86-64 все в стеке имеет ширину 8 байтов вместо 4.   -  person Peter Cordes    schedule 24.11.2017
comment
@PeterCordes Помещение [esp] и [esp+4] в стек с последующим вызовом main не работает. Любые идеи?   -  person Nivolas    schedule 24.11.2017
comment
push изменяет esp. Кроме того, main нужен указатель на argv, а не на первый элемент. Сам массив находится прямо в стеке. lea eax, [esp+4] / push eax / push [esp] может сработать, если я все правильно понял. О, esp должно быть выровнено по 16B, прежде чем вы вызовете main. В зависимости от программы, если вы этого не сделаете, может произойти ошибка сегментации. Таким образом, вы должны sub esp, 8 перед отправкой еще 2 аргументов, потому что ABI гарантирует, что esp выровнено по 16B при запуске процесса. Кроме того, обычный запуск CRT передает envp в main. Я предполагаю, что ваш main не ищет третий аргумент envp?   -  person Peter Cordes    schedule 24.11.2017
comment
Поскольку это на самом деле 32-битный вопрос, он не является точной копией stackoverflow.com/questions/35864291/, но он по-прежнему тесно связан.   -  person Peter Cordes    schedule 24.11.2017
comment
@PeterCordes Нет, он не ищет третий аргумент. То, что вы предложили, не работает. Может быть, я неправильно понимаю, как мне сбросить esp? после каждого нажатия мне нужно выполнить sub esp, 4, чтобы в итоге он оставался там, где начал?   -  person Nivolas    schedule 24.11.2017
comment
Нет, это будет пропускать дополнительные 4 байта после каждого нажатия. Помните, что push [esp] равно tmp = [esp] / esp -= 4 / [esp] = tmp, поэтому одно нажатие изменяет смещение, необходимое для доступа к другим элементам в стеке, на +4.   -  person Peter Cordes    schedule 25.11.2017
comment
Я думаю, что у меня была ошибка в том, что я предложил сначала: я отправил две копии argv, потому что использовал неправильное смещение для 2-го нажатия. lea eax, [esp+4] / push eax / push [eax-4] могут работать, но используйте отладчик, чтобы увидеть, что вы нажимаете и куда указывает esp на каждом шаге. (Обратите внимание, что я использовал [eax-4] вместо [esp+4] из соображений эффективности и потому, что так проще сделать правильно: мы использовали lea для установки eax до того, как начали изменять esp.)   -  person Peter Cordes    schedule 25.11.2017
comment
И кстати, я нашел 32-битный дубликат для вашего вопроса с гораздо более подробным ответом (ранее я редактировал дубликат). stackoverflow.com/ вопросы/16721164/. Прочтите ее, она покажет вам, как просматривать память с помощью GDB.   -  person Peter Cordes    schedule 25.11.2017
comment
Мне было любопытно, поэтому я попробовал код из моего последнего комментария. Это тоже работает. (Второму push требуется push dword [eax-4] для указания размера операнда, но все смещения правильные. Я протестировал его с помощью простого main, использующего printf, поэтому мне пришлось связать с gcc -m32 crt-replacement.o print-args.c -nostdlib -lc). В любом случае, ваш код эквивалентен, красив и компактен. (Но вы можете сохранить инструкцию mov, используя push esp, которая определена для передачи значения esp до push уменьшает его.).   -  person Peter Cordes    schedule 26.11.2017
comment
Кстати, после call main вы (или ваш профессор) должны использовать mov ebx, eax для передачи возвращаемого значения main в sys_exit вместо того, чтобы exit_status = все, что осталось в ebx. (На самом деле это 0, если вы не измените его перед вызовом main, потому что ebx сохраняется при вызове, и Linux инициализирует его до 0. Но ваша версия завершается со статусом = младший байт esp...)   -  person Peter Cordes    schedule 26.11.2017
comment
В любом случае, эта замена CRT вообще не работает для произвольных программ на C. Языковые функции, такие как atexit (зарегистрировать функцию, которая будет вызываться после main или exit()), зависят от кода CRT, чтобы проверить это (связано: функции выхода / системные вызовы. Он также не инициализирует libc при статической компоновке. (Но этот Makefile вообще не связывает libc.) В любом случае, просто мои 2 цента, что это минимальный стартовый код не делает всего того, что делает версия, предоставленная компилятором.   -  person Peter Cordes    schedule 26.11.2017