Неопределенная ссылка на `WinMain 'при компиляции программы Nasm в Windows (MinGW) - PullRequest
0 голосов
/ 24 октября 2018

Я хотел бы скомпилировать Пример Hello World NASM для Windows.

Я вставил приведенный выше код в файл main.asm и скомпилировал его в файл obj с помощьюэта команда:

nasm -fwin32 .\main.asm -o main.obj

После этого я хотел скомпилировать этот файл obj в exe-файл, например:

g++ .\main.obj -o main.exe -m32

Но я получаю эту ошибку:

C:/Program Files (x86)/mingw-w64/i686-8.1.0-posix-dwarf-rt_v6-rev0/mingw32/bin/../lib/gcc/i686-w64-mingw32/8.1.0/../../../../i686-w64-mingw32/lib/../lib/libmingw32.a(lib32_libmingw32_a-crt0_c.o):crt0_c.c:(.text.startup+0x39): undefined reference to `WinMain@16'

Что мне не хватает?Как можно исправить эту ошибку?

1 Ответ

0 голосов
/ 27 октября 2018

Эта программа Hello World пытается создать таблицу импорта PE вручную.Чтобы это работало, вам нужно тщательно проинструктировать компоновщик (разделы PE не привязаны к каталогам PE, idata - это просто имя).
В этом источнике сделаны дополнительные предположения (например, базовый адресизображение и необходимость в ЭЛТ).

Честно говоря, это просто чушь.Используйте компоновщик правильно, например Шут, показанный .
Если честно, весь раздел Википедии в лучшем случае носит информационный характер.
Короче говоря: никогда не используйте Википедию в качестве учебного пособия по программированию.

Немного краткой теории

Вы можете создать 32-разрядную консольную программу Windows в основном двумя способами:

  1. Использовать время выполнения C (CRT)
    Это позволяет вам использовать общие функции C (прежде всего printf).
    Существует два способа использования CRT:

    1. Статически
      Объектные файлы, полученные в результате компиляции исходного кода CRT, связаны с объектным файлом, являющимся результатом компиляции / сборки вашего исходного кода.
      Код CRT полностью встроен в ваше приложение.
      В этом сценарии ваша основная функция (main / WinMain / DllMain и варианты Unicode) вызывается из CRT, который сначала запускается с правильно установленной точкой входа PE).
      Для использования этого методавам нужны объектные файлы CRTих можно найти с помощью Visual Studio или MinGW (чтобы назвать двойки).
      Порядок выполнения таков: загрузчик Windows вызывает вашу точку входа PE, для нее установлено что-то вроде _mainCRTStartup, которое инициализирует CRT иЭЛТ вызывает вашу основную функцию.
    2. Динамически
      Основным dll CRT является msvcrt.dll для версии, поставляемой с установкой Windows, или msvcrtXX0.dll для версии, поставляемой с установкой Visual Studio (где XX зависит отверсия VS).
      У библиотеки CRT есть код инициализации и разрыва в точке входа DLL, поэтому, просто поместив его в таблицу импорта PE, CRT автоматически управляется.Порядок выполнения таков: загрузчик Windows загружает ваши PE-зависимости, в том числе CRT DLL (которая была инициализирована как указано выше), а затем вызывает вашу точку входа PE.
  2. Использовать только Windows API
    Windows API - это функции, предоставляемые операционной системой, которые в конечном итоге вызывает реализация CRT.

    Вы можете использовать Windows API и CRT (общий сценарий для графического приложения - статически связать CRT и использовать WinMain в качестве точки входа - где API Windows смешиваются с утилитой Cфункции) или только Windows API.
    При их использовании вы получаете меньше, быстрее и проще в исполнении.

Чтобы использовать 1.1, вам нужны объектные файлы CRT, и они обычно поставляются с компилятором (когда-то они были поставлены с Windows SDK, но теперь, когда VS свободен, Microsoft переместила их в VSпакет - неплохой, но VS на несколько порядков тяжелее SDK).
1.2 и 2. эти объектные файлы не нужны.
Обратите внимание, что совместимость компиляторов / ассемблеров / компоновщиков может быть неприятной мерой, особенно.lib механизм связывания внешних API (в основном файл libs - это способ заставить компоновщик находить функции, которые будут решаться загрузчиком во время выполнения - т.е. те, которые определены во внешней DLL).

Привет, мир!

Метод 2

Во-первых, написать Hello, World!используя метод 2., см. этот другой мой ответ .
Он был написан, когда компоновщик был доступен в Windows SDK, сегодня я использую GoLink .
Itявляется минималистским, очень простым в использовании компоновщиком.
Одним из ключевых моментов является то, что не нужны .lib-файлы , вместо этого вы можете передать ему путь к DLL, в котором находятся внешниефункции находятся.

Команда NASM та же, для связи используйте:

 golink /console /entry main c:\windows\system32\kernel32.dll hello.obj -fo hello.exe

Не проверено - при желании добавьте /largeaddressaware, если ваш код может обработать это

Этот пример для 64-битного программирования, он более сложен, чем 32-битный, но может быть полезен в любом случае.

Метод 1.2

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

BITS 32

GLOBAL _main

EXTERN printf
EXTERN exit

SECTION .text

_main:
 push strHelloWorld 
 call printf 
 add esp, 04h

 push 0
 call exit 


SECTION .data

 strHelloWorld db "Hello, world!", 13, 10, 0

Это довольно просто по сравнению с вики.
Чтобы сделать исполняемый файл:

nasm -fwin32 helloworld.asm -o helloworld.obj
golink /console /entry _main c:\windows\system32\msvcrt.dll helloworld.obj -fo helloworld.exe

Код Википедии создает секции .idata, в которых хранится таблица адресов импорта PE.
Это глупый ход, компоновщик используется для генерации этой таблицы на основе динамических зависимостей объектафайлы.
Чтобы сделать ссылку на эту программу, нам нужно:

  1. Сказать компоновщику, что базовый адрес 0x400000.Это можно сделать с любым компоновщиком (для golink используйте /base 0x400000).
  2. Сообщите компоновщику, что точка входа находится там, где начинается раздел .text.Я не знаю, может ли link.exe принять .text в качестве допустимого имени символа или позволяет ли указать точку входа относительно .text, но это кажется маловероятным.Голинк не допустит этого.Короче говоря, метка, вероятно, отсутствует.
  3. Скажите компоновщику, чтобы каталог Import указывал на раздел .idata.Я не знаю ни одного компоновщика, который позволил бы это (хотя он может существовать).

Короче, забудьте об этом.

Метод 1.1

Это то, что использовала ссылка , которую указал Джестер .
Код сборки такой же, как для 1.2, но вы используете MinGW для компоновки.

...