
CHAPTER 1. Unexpected guests.
It all started on that ill-fated morning, when the Project Manager announced that the project timeline should be quickly and decisively reduced by a month. More precisely, the project should be ready in 4 days. No, our PO is not a beast, and not at all like an
owl (except a little bit like a crow), it just happened. Well, if it is necessary, it is necessary, especially since the team (and I am the leading developer of the “C” team) was promised something tasty. The clock and calendar was Thursday, 11:00, by Monday the project should be ready.
To begin with, what we do. We are engaged in cinema automation - automatic and remote control of equipment, cinema automation, monitoring, video panels, and now also ticket and bar sales terminals. Specifically, the last point and is devoted to this article.
The project itself, which had to be completed before Monday, is a kind of layer between the main server on Scala and the iron payment terminal VeriFone VX 820 (in fact, there are more terminals, but for example we take only it). It is clear that no one will give us transactions just like that through him, therefore utilities and libraries of Sberbank / Arcus and UCS are used. Thus, the scheme of work in the end should be as follows:
')

Outwardly, it looks like this:

Also, this subsystem should be used on standard cash register machines that are all seen in any cinema at the cashiers.
According to internal tradition, we call each project of our team a name from the Norse mythology, the name Gefjon was chosen for this subsystem - the name of the goddess of fertility and abundance (a good name for a payment server, isn't it? Well, the legend about bulls cutting off the island ideally falls on the current architecture, cutting off work with equipment from a high-level language).
The format of incoming and outgoing messages is an HTTP server with JSON load. This is the best compromise between Scala, which is difficult to descend to isolating binary data from socket-threads and C, which is difficult to rise to transfer objects through the network. There are not so many possible operations that need to be operated on: payment, cancellation, refund, various types of reports, opening the service menu and ping. It looks nothing complicated. Since there are three banking systems (and the completion of the family is expected later), it was decided to divide the project into components:

Green painted blocks that we needed to do, blue - those that can not be changed and that the bank provides.
Since the main problems arose only with software from Sberbank, the article as a whole will be devoted to the pitfalls that we recounted with our rook.
CHAPTER 2. Lamb Roast

(photo: heaclub.ru)
... looks like this. The code of the prototype, which was written a few months ago to make it clear to all superiors, that we can work with banking applications, looked just like that.
char buf[BUF_KB * 2]; char * null; char * grep; #ifdef _WIN32_WINNT char * ptr; null = "nul"; grep = "findstr"; #else null = "/dev/null"; grep = "grep"; #endif sprintf(buf, "%s %"PRIi32"= %sops.ini >%s 2>%s || " "echo %"PRIi32"=9,6,PINPAD_TEST >> %sops.ini", grep, TERM_ARCUS_TEST_PINPAD, TERM_PATH, null, null, TERM_ARCUS_TEST_PINPAD, TERM_PATH); #ifdef _WIN32_WINNT ptr = buf; while (*ptr) { if (*ptr == '/') *ptr = '\\'; ptr++; } #endif
It is clear that for the Production option it was no good, so it was necessary in essence to write everything anew.
Each bank that provides libraries for working with the terminal usually provides two connection options: through the library functions (.so / .dll) or through a ready-made utility, which only needs to pass two values ​​— the type of operation and the amount (when needed). In theory, nothing complicated, just
char buffer[100]; sprintf(buffer, "%d %d", atoi(argv[1]), atoi(argv[2])); system(buffer);
The result of the operation will be placed in the file “e”, and the slip-check - in the file “p”. Just send these files to stdout with conversion to JSON, so that the HTTP server simply sends them up as payload without thinking about what is there.
But this article would not have been published if everything was so simple.
CHAPTER 4. Through the mountain and under the mountain
The initial implementation was a simple application call — the HTTP server called the necessary wrapper with unified parameters (for example, the X report was 4), and the utility, for example, gfj_pilot, ran sb_pilot with the parameter required for this operation (for example, the X report was 9) . Then the utility wrapper read from the e-file the result of the operation (for example, 2000 - “payment failure, repeat the operation”) and converted it into a universal error (for example, 3 - “Error reading or processing the card / account, repeat the operation”). After that, the “p” file was converted to base64 to avoid formatting and sent along with the result to stdout as JSON.
All this worked fine, until one day we were informed that ...
... it does not work under Windows.

Well, more precisely, Windows itself has no problems (except that the slip is generated in Cp-1251 encoding, and the console works in CP866). Just did not generate the "e" file. We launched the banking utility directly:
C:\banks\sber\sb_pilot>dir C . : B401-6B9D C:\banks\sber\sb_pilot 04.02.2019 12:28 <DIR> . 04.02.2019 12:28 <DIR> .. 31.01.2019 17:12 10 832 F12X24.BIN 31.01.2019 17:12 128 000 gate.dll 31.01.2019 17:12 72 192 loadparm.exe 31.01.2019 17:12 36 204 OPT0.R 31.01.2019 17:12 20 716 OPT1.R 31.01.2019 17:12 1 806 OPT3.R 31.01.2019 17:12 388 608 pilot_nt.dll 31.01.2019 23:06 463 pinpad.ini 31.01.2019 17:12 91 136 posScheduler.exe 31.01.2019 17:12 418 printers.ini 01.02.2019 16:51 91 646 sbkernel1902.log 31.01.2019 17:12 653 312 sbrf.dll 31.01.2019 17:12 840 192 SBRFCOM.dll 31.01.2019 17:12 3 142 656 sb_kernel.dll 01.02.2019 16:51 9 SESS.D 01.02.2019 16:51 715 SPLC.D 31.01.2019 17:12 72 192 upwin.exe 20 5 659 718 2 37 567 004 672 # (1) 10 (1000 ) C:\banks\sber\sb_pilot>loadparm.exe 1 1000 C:\banks\sber\sb_pilot>dir C . : B401-6B9D C:\banks\sber\sb_pilot 04.02.2019 12:28 <DIR> . 04.02.2019 12:28 <DIR> .. 04.02.2019 12:28 216 commerr.log 31.01.2019 17:12 10 832 F12X24.BIN 31.01.2019 17:12 128 000 gate.dll 31.01.2019 17:12 72 192 loadparm.exe 31.01.2019 17:12 36 204 OPT0.R 31.01.2019 17:12 20 716 OPT1.R 31.01.2019 17:12 1 806 OPT3.R 01.02.2019 18:51 1 349 p 31.01.2019 17:12 388 608 pilot_nt.dll 31.01.2019 23:06 463 pinpad.ini 31.01.2019 17:12 91 136 posScheduler.exe 31.01.2019 17:12 418 printers.ini 04.02.2019 12:28 92 218 sbkernel1902.log 31.01.2019 17:12 653 312 sbrf.dll 31.01.2019 17:12 840 192 SBRFCOM.dll 31.01.2019 17:12 3 142 656 sb_kernel.dll 01.02.2019 16:51 9 SESS.D 01.02.2019 16:51 715 SPLC.D 31.01.2019 17:12 72 192 upwin.exe 19 5 659 029 2 37 567 008 768 C:\banks\sber\sb_pilot>
Indeed, there is no “e” file. Stone in the direction of Sberbank # 1. We are writing a letter to the Savings Bank (later we received the answer that this should be so), and since there is no time for correspondence and we need to start right now, we are looking for workarounds for obtaining the result.
04.02 12:28:55 SBKRNL: Failed to open device \\.\COM1, err 2 04.02 12:28:56 SBKRNL: Failed to open device \\.\COM1, err 2 04.02 12:28:56 SBKRNL: Result = 0 04.02 12:28:56 GATE: unlock:'00000054' 04.02 12:28:56 GATE: lock:'00000054' 'UPOSWINMUTEX2' 04.02 12:28:56 GATE: unlock:'00000054' 04.02 12:28:56 LOADPARM: Unloading GATE.DLL... 04.02 12:28:56 GATE: SB_KERNEL.DLL is unloaded 04.02 12:28:56 LOADPARM: GATE.DLL unloaded
Yeah, the result can be obtained from the log sbkernelGGMM.log. It’s inconvenient, plus there isn’t a card hash to subsequently tie up “Thank you” from the savings bank. No good.
You will have to connect to the pilot_nt.dll library and import functions from it. Everything would be fine, but ... Stone towards Sberbank # 2: under Linux there is no such library, you will have to create two different applications for different platforms - for linux you need to call the utility sb_pilot (analog loadparm.exe, by the way stone # 3 for the different name of the utility under different platforms ) under windows connect to pilot_nt.dll library.
CHAPTER 5. Riddles in the dark
On the clock 19:00.
Sberbank is a large company, most software solutions are manufactured according to GOST and formal documents. We climb into the catalog that Sberbank supplies with libraries:
Sberbank$ ls -l Docs 30160 drwx------ 2 alex alex 4096 17 19:31 FAQ -rw-rw-r-- 1 alex alex 3398465 9 2018 UPOS ().docx -rw-rw-r-- 1 alex alex 1182078 9 2018 UPOS .docx -rw-rw-r-- 1 alex alex 853504 9 2018 .doc drwx------ 3 alex alex 4096 31 17:11 -rw-rw-r-- 1 alex alex 5280787 9 2018 POS-.docx -rw-rw-r-- 1 alex alex 1149640 9 2018 .docx drwx------ 2 alex alex 4096 28 2018 UPOS drwx------ 2 alex alex 4096 28 2018 -rw-rw-r-- 1 alex alex 3451601 9 2018 ().docx -rw-rw-r-- 1 alex alex 1956196 9 2018 .docx -rw-rw-r-- 1 alex alex 1043161 9 2018 ()_().docx -rw-rw-r-- 1 alex alex 4348157 9 2018 POS-.docx -rw-rw-r-- 1 alex alex 3970267 9 2018 .docx drwx------ 3 alex alex 4096 28 2018 -rw-rw-r-- 1 alex alex 2644702 9 2018 POS-.docx drwx------ 2 alex alex 4096 28 2018 -rw-rw-r-- 1 alex alex 1558211 9 2018 .png
A lot of good, but we are only interested in the directory for developers:
Sberbank$ ls -l Docs/\ \ \ / 8704 -rw-rw-r-- 1 alex alex 47105 9 2018 1C.docx -rw-rw-r-- 1 alex alex 1824 9 2018 cardtype.h -rw-rw-r-- 1 alex alex 2590378 9 2018 cr_ttk_protocol_ru.rtf -rw-rw-r-- 1 alex alex 208 9 2018 deprtmnt.h -rw-rw-r-- 1 alex alex 16681 9 2018 errors.h drwx------ 6 alex alex 4096 28 2018 examples -rw-rw-r-- 1 alex alex 58575 9 2018 gate.h -rw-rw-r-- 1 alex alex 4218 9 2018 paramsln.h -rw-rw-r-- 1 alex alex 61693 9 2018 pilot_nt.h -rw-rw-r-- 1 alex alex 28160 9 2018 ReadTrack2.doc -rw-rw-r-- 1 alex alex 7417 9 2018 sbkernel.h -rw-rw-r-- 1 alex alex 144896 9 2018 sb_pilot.doc -rw-rw-r-- 1 alex alex 3525323 9 2018 ole- sbrf.dll.rtf -rw-rw-r-- 1 alex alex 46683 9 2018 gate.dll.chi -rw-rw-r-- 1 alex alex 255414 9 2018 gate.dll.chm -rw-rw-r-- 1 alex alex 814653 9 2018 gate.dll.pdf -rw-rw-r-- 1 alex alex 41618 9 2018 pilot_nt.chi -rw-rw-r-- 1 alex alex 241716 9 2018 pilot_nt.chm -rw-rw-r-- 1 alex alex 968753 9 2018 pilot_nt.pdf -rw-rw-r-- 1 alex alex 81 9 2018 .txt
A lot of waste paper, just in case, once again reread pilot_nt, from which we learn the following:
Table 1. Supported sb_pilot OS.
It turns out the utility under windows should still be called sb_pilot. Well, a stone in the direction of Sberbank # 4 for the discrepancy of its own documentation.
Transfer the results of the program.
At the end of the program, two text files are created - the exchange file and the check file.
The first is named e and is intended to pass the parameters of the operation to the calling program. The first line in this file contains the result code of the operation, and a comma-separated explanatory text message. Code 0 means successful payment, any other value - refusal or impossibility of payment.

Lazily we throw another stone and begin to study the documentation for connecting the library directly.
The order of the functions of the library
When paying (returning) a purchase with a bank card, the cash program should call the function card_authorize () from the Sberbank library by filling in the TType and Amount fields and specifying zero values ​​in the other fields. At the end of the function, it is necessary to analyze the RCode field. If it contains the value "0" or "00", the authorization is considered successful, otherwise rejected. In addition, you need to check the value of the Check field.
If it is not NULL, it must be sent to print (in non-fiscal mode) and then
delete by calling the GlobalFree () function. When closing the shift, the cash program should call the close_day () function from the Sberbank library by filling in the TType = 7 field and specifying zero values ​​in the remaining fields. Upon completion of the function, you must check the value of the Check field.
If the Check field is not NULL, it must be sent to print (in non-fiscal mode) and then deleted by calling the GlobaFree () function.
It sounds easy, even a header file is provided. Well, we connect it, we compile and ...
$ cat main.c && i686-w64-mingw32-gcc main.c -o main.a #include "pilot_nt.h" int main(void) { return 0; } In file included from main.c:1:0: pilot_nt.h:525:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:544:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:567:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:590:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:627:3: error: unknown type name 'auth_answer' auth_answer ans; ^ pilot_nt.h:668:3: error: unknown type name 'auth_answer' auth_answer ans;
Ummm ... What? Open pilot_nt.h:
#ifdef __cplusplus extern "C"{ #endif <...> /** * * , . */ struct auth_answer{ int TType; /**< [in] . ::OpetationTypes */ unsigned long Amount; /**< [in] */ char RCode[3]; /**< [out] */ char AMessage[16]; /**< [out] */ int CType; /**< [in,out] */ char* Check; /**< [out] , GlobalFree */ }; <...> struct auth_answer7{ auth_answer auth_answ; /**< [in, out] . . ::auth_answer */ <---- THIS char AuthCode[MAX_AUTHCODE]; /**< [out] . 7 . */ char CardID [CARD_ID_LEN]; /**< [out] . 25 . */ int SberOwnCard; /**< [out] */ };
Immediately, without looking at the stone for comments in Russian encoded in CP1251.
Well, the most serious stone: dear C ++ developers. If you write extern "C" - this means that the code inside the block must be compiled by the C-compiler. If you have NOT made a `typedef` structure, then each time it is mentioned as a type indication, you must write the` struct` keyword.
Patches a file for developers, substituting wherever the word `struct` is needed. Link to the `pilot_nt.dll` library. Victory, no? Run our application.
CHAPTER 6. From fire to the fire
Well, you understand, right? The app just crashes. Immediately, before the main. We meditate; we add the NIH analogue of the errno function for windows: GetLastError (stone # 3 towards Microsoft, the first two for encodings).
C:\banks\sber\WIN>sb_pilot.exe 1 1000 E: !g_sblibrary (0xc0000096)
0xc0000096? Shouldn't GetLastError return an adequate error code?
For a complete list of error codes, see System Error Codes.
Yeah, open the article at the
link :
The following topics provide lists of system error codes. These values ​​are defined in the WinError.h header file.
- System Error Codes (0-499) (0x0-0x1f3)
- System Error Codes (500-999) (0x1f4-0x3e7)
- System Error Codes (1000-1299) (0x3e8-0x513)
- System Error Codes (1300-1699) (0x514-0x6a3)
- System Error Codes (1700-3999) (0x6a4-0xf9f)
- System Error Codes (4000-5999) (0xfa0-0x176f)
- System Error Codes (6000-8199) (0x1770-0x2007)
- System Error Codes (8200-8999) (0x2008-0x2327)
- System Error Codes (9000-11999) (0x2328-0x2edf)
- System Error Codes (12000-15999) (0x2ee0-0x3e7f)
Great, we got an undocumented error, throw a stone and open the all-knowing google:
The essence of the error is that some subroutine uses one of the instructions.
- _inp ()
- _inpw ()
- _inpd ()
- _outp ()
- _outpw ()
- _outpd ()
The use of which is prohibited under NT-cores, as they are trying to work with the parallel port directly. Apparently, this code is called in the library initializer, i.e. the library at startup wants to poll the ports for the presence of devices, but the NT core requires work through the driver.
Hopeless situation?
CHAPTER 8. Spiders and flies
22:00 Just in case, the idea arises to check that this is not due to the fact that we are using cross-compilation with Linux using mingw. At the same time, we understand that Sberbank supplies only a 32-bit application, so linking with a 64-bit application will not work, okay, but we still run the stone in the direction of Sberbank for the 32-only version in 2019m.
It is given : installed in virtualbox windows 7;
Necessary : install Visual Studio and copy MVP.
We go to the Microsoft website, download Visual Studio 2017. We take a community license, as we take it for verification, for business a license will be purchased if it takes off.
Download a few hundred megabytes and ...
We see that our version of the OS (Windows 7) is not supported.
Ok, we go to all sorts of obscene sites, we are looking for Visual Studio 2008, download a few hundred megabytes again and ...
Get the iso file.
Okay, let's try to install Daemon Tools 10 (as this is the version that the site offers) to insert this virtual disk.
Run the downloaded binary. Misfire, requires .NET Framework 4.5, download, set.
We launch the downloaded binary, the installation has begun, the bootloader says that it needs 4.5.2, download, install.
We launch the downloaded binary, the installation has begun, the bootloader says that it will not go anywhere until we install security update KB3033929, download, install.
And we get a slap in the face from Microsoft in the form of a message:

We violently throw a very sharp stone in the direction of Microsoft, download the old Daemon Tools from the torrents, successfully unpack Visual Studio, install, finally (00:00) compile MVP, we get the same error. Well, the version was good, but it didn't grow together.
CHAPTER 11. On the threshold
We write to the second programmer, who at this moment urgently completes the server and the registration procedure. He recalls that there is a git
repository that on the NT connects this library and works with it.
Suspiciously looking at the repository, download it, compile and run. Works.

Look at the code even more suspiciously. The code is identical, except that it is written in C ++ and not C.
We understand that the language has nothing to do with it. We look at the Sberbank libraries, which are pulling code behind them.
We see the last commit.
And here we are waiting for another surprise.
It turns out that the versions of the Sberbank library may be different. The last commit increases the version from 23 to 27. We copy to our test computer the version from the book - WORKS!
We check all the archives sent by Sberbank, compare versions and build a label:
Ok, now we will live. On those systems where it costs 26, we will upgrade to 29 or 27 and everything will take off.
We throw a stone # 9 in the direction of Sberbank for having broken behavior on NT systems.
CHAPTER 12. What was waiting for them inside
Not enough "e" file? It does not matter, we take patched headers, we dynamically link with the library to correctly return an error, write a code that simply writes the return code from the function to the file “e”, let's call the binary sb_pilot.exe and ...
To work it works.
That's just the version for the system "Kriptera" does not create a "p" file.
Sad look at the dripping blood on the knuckles and on the dent in the wall.To begin with, what is the system "Kriptera."
Cryptera is a Danish company that produces encryption equipment / security equipment / keys, etc. I think you all saw one of the copies of their products:

So, Sberbank uses their crypto module for pinpads and releases a special “patched” library, in which, as we already understood, the file “p” is not created. We write about this in Sberbank and in a few days we will receive the answer that “under the original system, the file“ p ”will be created, but under the patched system on the Crypter - no”. We'll give them a stone # 10 in a few days, because you need to work now.
Fortunately, or unfortunately, the functions that we use to conduct operations return the structure already mentioned:
struct auth_answer{ int TType; unsigned long Amount; char RCode[3]; char AMessage[16]; int CType; char* Check; };
Oh, great, the check is already there, we can save it to a file on our own or directly output it to JSON
printf("%s\n", answer.Check);
And we get the crash of the application due to treatment by invalid pointer.
CHAPTER 14. Fire and Water
4:00 We carry out Seth Bandha Sarvangasana to calm down, and carefully read the manual:
[out] check image, GlobalFree should be released in the calling program
What does this give us? A lot of things. First, once the pointer needs to be cleaned with GlobalFree, it is sallocated with
GlobalAlloc . Therefore, it does not issue a pointer to memory, as it was in the 16bit version, but an object number with the semantically declared type HGLOBAL, which can be fed in the GlobalSize function to get the size of the selected block and GlobalLock to block the piece of memory, but get the original pointer. By the way, Stone # 6 in the direction of Microsoft for NIH malloc and free, which are in the standard library.
printf("%s\n", GlobalLock(answer.Check));
And still get the fall. Okay, so what is GlobalSize? Zero? Kind of weird.
We check other functions that should also give a slip - we see the same picture.
It comes to mind that it is possible to independently generate a slip according to the data that the coolest payment function can issue (yes, Sberbank’s functions are called card_authorize2..14, I won’t throw a stone for it):
struct auth_answer14 { auth_answer ans; char AuthCode[MAX_AUTHCODE]; char CardID[CARD_ID_LEN]; int ErrorCode; char TransDate[TRANSDATE_LEN]; int TransNumber; int SberOwnCard; char Hash[CARD_HASH_LEN]; char Track3[CARD_TRACK3_LEN]; DWORD RequestID; DWORD Department; char RRN[MAX_REFNUM]; DWORD CurrencyCode; char CardEntryMode; char CardName[MAX_CARD_NAME_LEN]; char AID[MAX_AID_ASCII_LEN]; char FullErrorText[MAX_FULL_ERROR_TEXT]; DWORD GoodsPrice; DWORD GoodsVolume; char GoodsCode[MAX_GOODS_CODE+1]; char GoodsName[MAX_GOODS_NAME]; }; PILOT_NT_API int card_authorize14( char *track2, struct auth_answer14 *auth_answer, struct payment_info_item *payinfo );
We try to select fields ... We find out that only one thing separated us from happiness - the Last Name and First Name of the card carrier. Without them, a slip is not considered legal :Details: ID of an ATM, electronic terminal or other technical device intended for making transactions using payment cards; type of operation; transaction date; transaction amount; currency of operation; amount of commission authorization code; payment card details.
It is a pity, but to form a legitimate slip with the data that we have will fail.We will rummage in the documentation once again.Find an example that Sberbank supplies in the “examples” directory std::cout << "Authorization completion finished with code '" << result << "'" << std::endl; std::ofstream file(CHEQUE_FILENAME); file << argument.auth_answ.Check; file.close(); if (argument.auth_answ.Check) { std::cout << "Cheque saved to file " << CHEQUE_FILENAME << std::endl;
Just displays the text, located on the index. But after all, we have already seen that it does not work like this ... Just in case, let's compile their example and run. Departure at the line `file << argument.auth_answ.Check;`, well, Sberbank, hold the stone # 11 for non-working examples.Seven o'clock You can already write to the developers of another wrapper, which several years ago was written in Delphi. We get the answer that everything works for them. We look for the basis of their wrappers and find it on github : TAuthAnswer = packed record TType: integer; Amount: UINT;
Simple type conversion to pointer without any function calls.We begin to suspect evil spirits.CHAPTER 17. Thunderstorm erupted.
People are starting to return to the office, nodding sympathetically. PO doesn’t look very funny after hearing the latest news.Here I recall one detail. When we displayed the fields of structure # 14 to see their values, then one byte of each line was cut off. On the one hand it is, on the otherAttention!In the auth_answer14 structure, the product name is one character shorter than the gate.dll TGoodsData. Fix this error as standard
Maybe this is still connected with ... Aterrible guess overshadows the brain like lightning. We declare the structure as typedef struct __attribute__((packed)) { int TType; unsigned long Amount; char RCode[3]; char AMessage[16]; int CType; char* Check; };
And ...Nothing changes.All the same Size = 0, Still Lock = NULL.Pain.Ashes.Involuntarily you are looking for a comfortable beam on the ceiling with your eyes, such as to withstand the weight. After so many non-stop hours of coding and studying the documentation, slender rows of bytes float before your eyes. And what if the output bytes, which are generally returned? u32 i; for (i = 0; i < sizeof(answ); i++) { printf("%02x ", *((u8 *)&answ + i)); } printf("\n"); C:\banks\sber\sb_pilot>sb_pilot.exe 1 1000 01 00 00 00 e8 03 00 00 30 00 00 ce e4 ee e1 f0 e5 ed ee 00 00 00 00 00 00 00 00 02 00 00 00 f8 6c 7a 00 00
`30 00 00 ce` - which means that Sberbank still uses Packed structures. That's just in the headers about this is not a word. Therefore, the examples do not work, so it is impossible to get a pointer to the text at the end - because it is broken due to a shift of 1 byte. A huge and spiny stone in the direction of Sberbank!And then one maaaalen little thing caught my eye. 4 + 4 + 3 + 16 + 4 + 4 = 35. And then there are 36 bytes, Obelix.Once it is 36 bytes, the compiler still aligns the structure. So an extra byte is still inserted between RCode and AMessage. But why? After all, we specified `__packed__`!CHAPTER 18. Return journey
The reasons that the alignment is still included appeared in 2012: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 . A bug was fixed only in GCC 8 (a stone for 6 years of failure!), Which is not yet possible to upgrade to. Fortunately, there is a workaround: -mno-ms-bitfields
We will not now disassemble the mechanism of this flag, just give it to the compiler:
Slip! Darling! I missed you, I will not even swear because of krakozyabr, I already threw a stone for it.And finally, we’ll feed a stone to Microsoft, for giving GlobalSize / Lock zeroes to invalid pointers.CHAPTER 19. The Last Chapter
To minimize the number of ifdefs for the sb_pilot interlayer, we wrote a separate application that completely simulates the linux version of sb_pilot. Thus, leaving the code of layer # 1 the same, leaving only one condition: #if defined(BXI_OS_GLX) #define GFJ_PILOT_EXECUTABLE "./sb_pilot" #elif defined(BXI_OS_WIN) #define GFJ_PILOT_EXECUTABLE "./sb_pilot.exe" #endif
Battle results:- Sberbank: 12 stones
- Microsoft: 7 stones
- GCC: 1 stone
Achivka memory on our command board: