This is an old revision of the document!
Table of Contents
Lab 2: Configuration of the Visual Studio Community 2019 for dll libraries written in MASM.
Creating the project with dll library written in assembler
In the beginning, we can create a project in which the main program will be written in HLL (High-Level Language) like C++, and the library written in assembler will be linked dynamically (dll).
Creating of the new project
During this laboratory, we'll show how to create and use dll (dynamically linked library) written in assembler in that application written in C++ We start by creating a new C++ project.
We'll create the console application, written in C++, for execution in the Windows operating system.
We need to define that name for the project. We suggest the name “Lab2_dll_cpp”. All paths can be left with default values.
Adding the assembly part to the solution
To add a library written in assembler to our solution, we add a new project to the solution. Right-click on the solution and select add new project.
We create our new assembler project as a C++ compatible library project, running under the Windows operating system. From the list of available options, we select the Dynamic-Link Library (DLL).
We name the project e.g. “JaAsm” and create it leaving the default path.
Setting dependencies in the solution
The next step is to set up the project dependencies so that compiling the C++ console application automatically forces the assembly library to be compiled. To do this, right-click on the C++ console project, select the “Build dependencies” menu, and then select “Project dependencies”.
In the newly opened window, select our “JaAsm” library and click “OK”.
Changing the type from a C++ library to an assembler one
First, you need to remove the automatically created C++ project files. To do this, simply select the header and source files, then right-click and choose “Remove” or click Delete key.
For the library, it is necessary to customize the compilation as an MASM assembler file. To do this, right-click the library project and select “Build dependencies” from the list, then “Build customization”.
In the newly opened window, select “masm” and confirm our selection with “OK”.
The next step is to add the source files in the assembler and the definition file to the library. To do this, right-click on the “JaAsm” library again, then select “Add” and “New item”.
We repeat this operation twice, each time selecting “Utility” and the text file type, giving the names: “JaDll.asm” and “JaDll.def”.
In order for the compiler to know that the created assembler file is associated with MASM, its type must be indicated. This step is necessary when we use a C++ source file and only change its extension to *.asm. By clicking the work key on the *.asm file, we select properties and set the file type to Microsoft Macro Assembler.
Setting the compilation mode
Compilation should be done in 64-bit mode (x64). To do this, set this mode in the solution options. Right-click on our solution and select “Properties”.
In the newly opened window, go to the “Configuration properties” → “Configuration” tab and then click “Configuration manager”.
In the new window, we indicate the x64 platform everywhere (if a given option is unavailable, we select “New” from the available list, and then create the x64 platform by copying the settings from Any CPU). You should also enable compilation (Build) for both parts of the solution.
After that, we make sure once again that the platform indicated everywhere is x64 and close the window. The “x64” option should automatically be set in the main menu bar of Visual Studio, but it is worth checking and modifying it if necessary.
Final setup of the assembly library
We proceed to configure the library. To do this, right-click on the library and select “Properties”.
In the newly opened window, go to “Linker” → “Input” and in the “Module definition file” field, enter the name of our file “*.def”. In our case, “JaDll.def”. Confirm the entry.
The final step in the configuration is to disable the library entry point. To do this, go to the “Linker” tab and select “Advanced” and select “Yes (/NOENTRY)” in the “No entry point” field. Save the settings and close the window.
Wykorzystanie biblioteki dll napisanej w asemblerze
Biblioteka asemblerowa dll
Biblioteka dll zawiera funkcje, które można użyć w innych programach. Pierwsza wersja naszej biblioteki będzie udostępniała jedną funkcję, zwracającą stałą wartość. Wartość tę następnie odczytamy w pliku C++. Przykładowa zawartość pliku asemblerowego (JaDll.asm) może wyglądać następująco.
.code MyProc proc mov RAX, 2021 ret MyProc endp end
Z plikiem kodu programu nierozerwalnie związany jest plik definicji (JaDll.def). Plik taki musi zawierać nazwę biblioteki DLL jako agrument dyrektywy LIBRARY, oraz listę nazw funkcji dostępnych dla innych modułów poprzedzonych dyrektywą EXPORTS. Aby nasza funkcja MyProc była widziana przez program C++ musimy ją zadeklarować jako eksportowaną. Plik JaDll.def w naszym projekcie może wyglądać następująco.
LIBRARY JaAsm EXPORTS MyProc
Często chcemy wykorzystać przekazane do funkcji parametry. Przypomnijmy, że w 64-bitowej konwencji wywołań pierwsze cztery parametry są umieszczane bezpośrednio w rejestrach procesora. Szczegóły są opisane w instrukcji do pierwszego ćwiczenia. I tak, dwa parametry typu całkowitego są umieszczone w rejestrach RCX i RDX. Funkcja, któa zwraca sumę dwóch argmentów całkowitych wyglądałaby w następujący sposób.
... MyProc proc mov RAX, RCX add RAX, RDX ret MyProc endp ...
Argumenty typu zmiennoprzecinkowego należy przekazać przez rejestry XMM0 i XMM1, a wynik zwrócić przez XMM0. Oczywiście należy użyć instrukcji rozszerzeń MMX/SSE.
... FloatProc PROC addsd xmm0, xmm1 ret FloatProc ENDP ...
Odwołanie do biblioteki dll z programu C++
Aby program napisany w C++ mógł wykorzystać funkcję z biblioteki należy wykonać kilka kroków.
- Przygotować typ funkcji zgodny z jej późniejszym wywołaniem,
- Załadować bibliotekę funkcją LoadLibrary uzyskując jednocześnie jej uchwyt,
- Odczytać aktualny adres interesującej nas funkcji wywołując GetProcAddress,
- Użyć funkcji odwołując się do jej adresu.
Przykładowy kod mógłby wyglądać tak:
#include <windows.h>
#include <iostream>
typedef int(_stdcall* MyProc)();
HINSTANCE dllHandle = NULL;
int main()
{
dllHandle = LoadLibrary(TEXT("JaAsm.dll"));
MyProc procedura = (MyProc)GetProcAddress(dllHandle, "MyProc");
std::cout << procedura();
}
Użycie funkcji z parametrami wymaga nieco zmodyfikowanych definicji typu. Należy także zwrócić uwagę na typ wartości zwracanej przez funkcję. Pamiętać należy, że w przypadku liczb zmiennoprzecinkowych, rozmiar rejestrów XMM0, XMM1 to 64 bity. Odpowiada to wartości zmiennoprzecinkowej podwójnej precyzji, czyli w języku C++ typowi “double”.
... typedef int(_stdcall* MyProc)(int, int); typedef double(_stdcall* FloatProc)(double, double); ...
Zadanie do zrealizowania w ramach laboratorium
Należy napisać bibliotekę DLL w asemblerze i program w C++ demonstrujący działanie tej biblioteki.
Biblioteka DLL ma udostępniać funkcje wykonujące i zwracające wynik podstawowych obliczeń arytmetycznych (dodawanie, odejmowanie, mnożenie i dzielenie) na parach liczb przekazanych jako parametry ich wywołania. Funkcje powinny mieć wersję stało- i zmiennoprzecinkową (w sumie osiem funkcji).
Program w C++ ma wywoływać wszystkie funkcje przekazując przykładowe wartości parametrów i wyświetlić wyniki na ekranie. Nie jest wymagany interfejs użytkownika - wartości do obliczeń mogą być umieszczone w kodzie programu.




























