Pelles C forum
Assembly language => Assembly discussions => Topic started by: Vortex on October 01, 2023, 01:30:33 PM
-
Here is an example of calling the undocumented function MessageBoxTimeout at runtime.
include Test.inc
CallFunc64 PROTO :QWORD,:QWORD,:QWORD
.data
user32 db 'user32.dll',0
MyFunc db 'MessageBoxTimeoutA',0
message db 'This message box will be destroyed after 4 seconds.',0
caption db 'Hello',0
; int MessageBoxTimeoutA(HWND hWnd, LPCSTR lpText,
; LPCSTR lpCaption, UINT uType,
; WORD wLanguageId, DWORD dwMilliseconds);
array dq 0,OFFSET message,OFFSET caption
dq MB_ICONWARNING,LANG_NEUTRAL,TIMEOUT
.code
main PROC PARMAREA=4*QWORD
LOCAL hMod:QWORD
invoke LoadLibrary,ADDR user32
mov hMod,rax
invoke GetProcAddress,rax,ADDR MyFunc
invoke CallFunc64,rax,6,ADDR array
invoke FreeLibrary,hMod
invoke ExitProcess,0
main ENDP
END main
-
Thanks Vortex!
Very neat, always wanted something like this, never knew it was there.
Hope I can get it to work in C.
John Z
-
Hope I can get it to work in C.
John Z
Hi JJ,
This will get you there. This is the A version.
If you want the W versions, it's only minor edits.
#include <windows.h>
#include <stdio.h>
long __stdcall MessageBoxTimeoutA (HWND, char*, char*, int, int, int);
long __stdcall MessageBoxTimeoutA (HWND hwnd,char* Txt,char* Title,int MBType,int Langid,int Milliseconds)
{
typedef int (__stdcall *MBTYPEDEF)(HWND, char *, char *, long, int, int);
static MBTYPEDEF MsgBoxTimeout;
static int Result;
HMODULE HMOD_USER32 = LoadLibrary("user32.dll");
MsgBoxTimeout = (MBTYPEDEF)GetProcAddress(HMOD_USER32, "MessageBoxTimeoutA");
Result=MsgBoxTimeout(hwnd,Txt,Title,MBType,Langid,Milliseconds);
return Result;
}
#define MBTYPE MB_YESNOCANCEL|MB_SETFOREGROUND|MB_SYSTEMMODAL|MB_ICONINFORMATION
// Demo
int main(int argc, char *argv[])
{
printf("% d",(int)MessageBoxTimeoutA(NULL, "This works with all compilers...", "Title Goes Here", MBTYPE, 0, 4000));
return EXIT_SUCCESS;
}
-
Hi Kevin,
Thanks for your code. Checking the object module with Agner Fog's objconv tool, I saw that the stack was balanced after calling MessageBoxTimeoutA. This should not happen as MessageBoxTimeoutA is a STDCALL function. The code is built as 32-bit in this case.
_main PROC NEAR
push 4000
push 0
push 69699
push offset @1044
push offset @1043
push 0
call _MessageBoxTimeoutA
add esp, 24
push eax
push offset @1042
call _printf
add esp, 8
xor eax, eax
ret
_main ENDP
The solution is inserting the __stdcall statment just before MessageBoxTimeoutA :
/*long __stdcall MessageBoxTimeoutA (HWND, char*, char*, int, int, int);*/
long __stdcall MessageBoxTimeoutA (HWND hwnd,char* Txt,char* Title,int MBType,int Langid,int Milliseconds)
{
typedef int (__stdcall *MBTYPEDEF)(HWND, char *, char *, long, int, int);
static MBTYPEDEF MsgBoxTimeout;
static int Result;
HMODULE HMOD_USER32 = LoadLibrary("user32.dll");
MsgBoxTimeout = (MBTYPEDEF)GetProcAddress(HMOD_USER32, "MessageBoxTimeoutA");
Result=MsgBoxTimeout(hwnd,Txt,Title,MBType,Langid,Milliseconds);
return Result;
}
Objconv reports now the correct output :
_main PROC NEAR
push 4000
push 0
push 69699
push offset @104
push offset @104
push 0
call _MessageBoxTimeoutA@24 ; no stack balance after the call to MessageBoxTimeoutA
push eax
push offset @1042
call _printf
add esp, 8
xor eax, eax
ret
_main ENDP
-
Hello,
Pelle's Podump tool can be used to disassemble the object module :
podump.exe /DISASM MBoxTimeOut.obj
Dump of MBoxTimeOut.obj
File type: OBJ
_MessageBoxTimeoutA@24:
[00000000] 55 push ebp
[00000001] 89E5 mov ebp,esp
[00000003] 53 push ebx
[00000004] 56 push esi
[00000005] 57 push edi
[00000006] 8B5D08 mov ebx,dword ptr [ebp+8]
[00000009] 8B750C mov esi,dword ptr [ebp+C]
[0000000C] 8B7D10 mov edi,dword ptr [ebp+10]
[0000000F] 6800000000 push @1025
[00000014] FF1500000000 call dword ptr [__imp__LoadLibraryA@4]
[0000001A] 6800000000 push @1027
[0000001F] 50 push eax
[00000020] FF1500000000 call dword ptr [__imp__GetProcAddress@8]
[00000026] A300000000 mov dword ptr [@1023],eax
[0000002B] FF751C push dword ptr [ebp+1C]
[0000002E] FF7518 push dword ptr [ebp+18]
[00000031] FF7514 push dword ptr [ebp+14]
[00000034] 57 push edi
[00000035] 56 push esi
[00000036] 53 push ebx
[00000037] FF1500000000 call dword ptr [@1023]
[0000003D] A300000000 mov dword ptr [@1024],eax
[00000042] 5F pop edi
[00000043] 5E pop esi
[00000044] 5B pop ebx
[00000045] 89EC mov esp,ebp
[00000047] 5D pop ebp
[00000048] C21800 ret 18
[0000004B] 0F1F440000 nop [eax+eax+0]
_main:
[00000050] 68A00F0000 push FA0
[00000055] 6A00 push 0
[00000057] 6843100100 push 11043
[0000005C] 6800000000 push @1044
[00000061] 6800000000 push @1043
[00000066] 6A00 push 0
[00000068] E893FFFFFF call 00000000
[0000006D] 50 push eax
[0000006E] 6800000000 push @1042
[00000073] E800000000 call _printf
[00000078] 83C408 add esp,8
[0000007B] 31C0 xor eax,eax
[0000007D] C3 ret
SUMMARY
8 .bss
10 .debug$F
28 .drectve
53 .rdata
7E .text
-
Here is an example of calling the undocumented function MessageBoxTimeout at runtime.
include Test.inc
CallFunc64 PROTO :QWORD,:QWORD,:QWORD
.data
user32 db 'user32.dll',0
MyFunc db 'MessageBoxTimeoutA',0
message db 'This message box will be destroyed after 4 seconds.',0
caption db 'Hello',0
; int MessageBoxTimeoutA(HWND hWnd, LPCSTR lpText,
; LPCSTR lpCaption, UINT uType,
; WORD wLanguageId, DWORD dwMilliseconds);
array dq 0,OFFSET message,OFFSET caption
dq MB_ICONWARNING,LANG_NEUTRAL,TIMEOUT
.code
main PROC PARMAREA=4*QWORD
LOCAL hMod:QWORD
invoke LoadLibrary,ADDR user32
mov hMod,rax
invoke GetProcAddress,rax,ADDR MyFunc
invoke CallFunc64,rax,6,ADDR array
invoke FreeLibrary,hMod
invoke ExitProcess,0
main ENDP
END main
Hi Vortex:
There is no "test.inc" file in the zip hence error
CallFunc64\Test.asm(1): fatal error: Can't open file 'Test.inc'.
-
Hi Robert,
My apologies, it's now fixed. New upload at the top.
-
Erol .. thanks for the insight and correction.
I've updated the code in my post.
-
Vortex, MrBCX,
Thanks very much! It will be very handy to now have this proc.
No longer need to thread out a dialog box and start a timer to kill it.
Very neat!'
John Z
-
Hi John,
Your method based on dialog boxes is safer. Not easy to predict if MS is deciding one day to remove a particular undocumented function.
-
... I can't really see the point/need for a message box that disappears after a while:
- If you have something (reasonably) important to say, don't you want confirmation it was seen?
- If it wasn't that important, why bother with a message box in the first place?
... anyway, not that important since it was an example for this example... ::)
-
Hi Pelle,
No worries, this is just a quick example. The LoadLibrary\GetProcAddress\Call Function method is useful to exit gracefully when a particular DLL is not everytime available.
-
No worries - I would not knowingly use any undocumented API feature in software released into the wild.
BUT
I see uses for it during development as part of a debug process. Plus the sample C code is informative.
John Z
-
What we learned, missing WINAPI leads problems.
; int WINAPI MessageBoxTimeoutA(HWND hWnd, LPCSTR lpText,
; LPCSTR lpCaption, UINT uType,
; WORD wLanguageId, DWORD dwMilliseconds);
x64 don't need it, only x86
-
Thanks to Pelle, the user32.lib import libraries are already providing the function MessageBoxTimeout :
D:\PellesC\Bin>podump.exe /EXPORTS ..\Lib\Win\user32.lib | findstr "MessageBoxTimeout"
User32.dll: MessageBoxTimeoutA (_MessageBoxTimeoutA@24)
User32.dll: MessageBoxTimeoutW (_MessageBoxTimeoutW@24)
D:\PellesC\Bin>podump.exe /EXPORTS ..\Lib\Win64\user32.lib | findstr "MessageBoxTimeout"
User32.dll: MessageBoxTimeoutA (MessageBoxTimeoutA)
User32.dll: MessageBoxTimeoutW (MessageBoxTimeoutW)
-
This will get you there. This is the A version.
If you want the W versions, it's only minor edits.
Thanks! This works very well, both modal and non-modal. Adding it to my 'Utilities.c' file. :)
John Z
-
Adding it to my 'Utilities.c' file. :)
Simple Tip:
If you have many 'utility' functions/procedures is better to create a static library project and put each related group of functions in a single separated file (C module). Then using the library you will link to the executable only the functions/procedures used, producing a smaller executable.
If all utility functions/procedures are coded in a single file (C module) probably the linker will put all of them, used and unused, in the executable producing a larger file. ;)
-
Frankie is right. It'a about the granularity concept. From Microsoft Macro Assembler's Programmer's Guide :
Granularity : The degree to which library procedures can be linked as individual blocks of code. In
Microsoft libraries, granularity is at the object-file level. If a single object file containing three
procedures is added to a library, all three procedures will be linked with the main program even if only
one of them is actually called.
-
I don't code much anymore and my memory is a bit faulty these days but I used this with VC++.
https://learn.microsoft.com/en-us/cpp/build/reference/gy-enable-function-level-linking?view=msvc-170
James
-
I don't code much anymore and my memory is a bit faulty these days but I used this with VC++.
https://learn.microsoft.com/en-us/cpp/build/reference/gy-enable-function-level-linking?view=msvc-170
James
since ml version 12 ( msvc 2013 ) is commandline option /Gy[-] separate functions for linker
so it support COMDAT
-
I don't code much anymore and my memory is a bit faulty these days but I used this with VC++.
https://learn.microsoft.com/en-us/cpp/build/reference/gy-enable-function-level-linking?view=msvc-170
I looked at this a long time ago, and may have forgotten some things, but IIRC the problem for me was that the COFF file format didn't really support this. It had to be implemented using somewhat fuzzy conventions.
An example: the C runtime is split up into individual files since this worked best a long time ago, when compilers/computers were slower and a MAKEFILE made sure the least amount of rebuilding were done. For me, mentally, I still like this split: one task per source file. One thing to focus on, even if the solution requires multiple functions to implement. Maybe it's just me...
-
Compilers use different approach to separate functions independently from the module they belong.
The GCC suite makes available the flag -ffunction-sections which splits the functions code in different segments in ELF format. For each function is created a subsection prepended by a period with the function name. I.e. for function:
int foo
{
return 42;
}
Will be coded as a .text subsegment .text.foo:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000 0000 0000 00 0 0 0
[ 1] .text PROGBITS 00000 0034 0000 00 AX 0 0 1
[ 2] .data PROGBITS 00000 0034 0000 00 WA 0 0 1
[ 3] .bss NOBITS 00000 0034 0000 00 WA 0 0 1
[ 4] .text.foo PROGBITS 00000 0034 001c 00 AX 0 0 4
[..]
This technique allows to easily merge all text sections back in the single text sections by having *(.text.*) spec along with *(.text) spec in built-in linker scripts.
Anyway a similar facility, named Grouped Sections, is available in MS-COFF format (available only in OBJ files), currently used for the debug sections in PellesC, where the separator used is the '$' sign:
Dump of EnumSymbols.obj
File type: OBJ
SUMMARY
20 .data
104B .debug$S
3AC .debug$T
51 .drectve
18 .pdata
1444 .rdata
14A .text
18 .xdata
MS defines Grouped sections as (https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#section-table-section-headers):
Grouped Sections (Object Only)
The "$" character (dollar sign) has a special interpretation in section names in object files.
When determining the image section that will contain the contents of an object section, the linker discards the "$" and all characters that follow it. Thus, an object section named .text$X actually contributes to the .text section in the image.
However, the characters following the "$" determine the ordering of the contributions to the image section. All contributions with the same object-section name are allocated contiguously in the image, and the blocks of contributions are sorted in lexical order by object-section name. Therefore, everything in object files with section name .text$X ends up together, after the .text$W contributions and before the .text$Y contributions.
The section name in an image file never contains a "$" character.
In analogy to the ELF format we can imagine something like:
Dump of foo.obj
File type: OBJ
SUMMARY
20 .data
104B .debug$S
3AC .debug$T
51 .drectve
18 .pdata
1444 .rdata
14A .text
15A .text.foo
18 .xdata
Of course considering this possible doesn't mean that the implementation is easy... :(
-
For me, mentally, I still like this split: one task per source file. One thing to focus on, even if the solution requires multiple functions to implement. Maybe it's just me...
That is what I try to maintain as well for any relatively large program. One 'major' task per source file. For Example if BASE64 conversions are needed then one source file for that, or export to ODS - one file. However it makes sense that one other source file contains small routines that are used in several major task source files. For example a character replacement routine or a left trim that is used in multiple other sources.
I didn't think about what happens with a routine being included that was written but never called....
John Z
-
Of course considering this possible doesn't mean that the implementation is easy... :(
Technically not hard, but there are other problems:
- how should this interact with the #pragma's code_seg, data_seg, and const_seg (I really don't know).
- many functions contain literal strings and/or static data, that will not be covered by this (referenced strings and most data are emitted at the end of the compilation, when associated function context is long lost; the data will be included by the linker, but then never referenced)
-
However it makes sense that one other source file contains small routines that are used in several major task source files. For example a character replacement routine or a left trim that is used in multiple other sources.
There are always multiple ways of doing things.
I have put all my truly general functions, the ones that span multiple projects, into a separate static library (and yes, each function in a separate source file). Linking with this library will pull in just what's needed.
Functions that are general for a single project (only), tend to be used in my experience (so not really a problem).
-
- how should this interact with the #pragma's code_seg, data_seg, and const_seg (I really don't know).
Well, the section flags in the Characteristics field of a section header defines the properties of the segment, executable, read-only, etc. This could help to classify it in a specific type: code, uninitialized data, read-only data, etc. This during the link identify the final standard MS-COFF segments (full list (https://learn.microsoft.com/en-us/cpp/build/reference/section-specify-section-attributes?view=msvc-170)).
When the pragma segment name is defined we have 2 options: still use the section grouping (i.e. <code_section_name>$<function_name>), or simply ignore it.
- many functions contain literal strings and/or static data, that will not be covered by this (referenced strings and most data are emitted at the end of the compilation, when associated function context is long lost; the data will be included by the linker, but then never referenced)
A solution could be to create a grouped data section also for collateral segments in the form <data_section_name>$<function_name>. This should help to determine if the segment should be included or not in the link.
Not easy, I confirm my assertion :(, but IMO the real point is: is it worth of? If you really want to implement the MS switch /Gy then yes. Else I don't know.
What I think can be really useful is to respect the alphabetic order of the grouped sections during the code emission, because could be helpful to the sequence positioning of data in the file.
-
but IMO the real point is: is it worth of?
I don't think so, since there are easy ways around it, but the question comes up once in a while ("since the big compilers can do it, why can't you?" yada, yada, yada...)
In some ways, a better idea would be to implement link-time optimization, but:
1) this makes much more sense for a language like C++, which will almost by definition produce many small functions (some likely identical at the machine code level), or when you build something huge like Word or Firefox.
2) this is a massive amount of work, and since C isn't C++ (yet), it's never going to happen (with Pelles C).
What I think can be really useful is to respect the alphabetic order of the grouped sections during the code emission, because could be helpful to the sequence positioning of data in the file.
If we talk about the same thing, the '$' grouping stuff requires sorting. This is (among other things) how various C runtimes collect data-blocks from different object files; use f.e. .somename$XA as the starting placeholder, .somename$XZ and the ending placeholder, and .somename$X<whatever> in different object files. When running, any data will be packed between .somename$XA and .somename$XZ (disregarding alignment, etc).
-
but IMO the real point is: is it worth of?
I don't think so, since there are easy ways around it, but the question comes up once in a while ("since the big compilers can do it, why can't you?" yada, yada, yada...)
I agree, it is very important when compiling for small memory systems, as embedded programming where the ffunction-sections switch is mandatory to keep low the memory footprint, and on very large compile jobs (C++ and other object oriented languages or large programs).
With careful structuring of source modules there should be no big problems.
What I think can be really useful is to respect the alphabetic order of the grouped sections during the code emission, because could be helpful to the sequence positioning of data in the file.
If we talk about the same thing, the '$' grouping stuff requires sorting. This is (among other things) how various C runtimes collect data-blocks from different object files; use f.e. .somename$XA as the starting placeholder, .somename$XZ and the ending placeholder, and .somename$X<whatever> in different object files. When running, any data will be packed between .somename$XA and .somename$XZ (disregarding alignment, etc).
Yes. I meant exactly that, and for that scope. :)
-
Yes. I meant exactly that, and for that scope. :)
OK, good. I think this sorting have been in my linker since version 1.0, before much of it was documented. Internally it's just "the weird dollar rule", but it was essential when linking objects and libraries from Microsoft (I can't remember exactly what broke without this "feature").