Author Topic: Macro version of printf  (Read 736 times)

Biterider

  • Moderator
  • Member
  • *****
  • Posts: 1041
  • ObjAsm Developer
    • ObjAsm
Macro version of printf
« on: June 21, 2022, 04:19:24 PM »
Hi
Recently I rewrote some older code to remove a windows dependency, mainly printf. This gave me the idea to think about whether there couldn't be a better way to do the job of printf.
Since printf's format string doesn't change in most cases, it's possible to find another approach to get a more performant result.
The idea is to find a similar way, sort of like HSE did in its PrintC macro.
The macro should parse the format string and generate the code according to the result of the parser.
MASM is ideal for this task provided we have all the necessary subroutines to get the job done.

Biterider

jj2007

  • Member
  • *****
  • Posts: 13311
  • Assembly is fun ;-)
    • MasmBasic
Re: Macro version of printf
« Reply #1 on: June 21, 2022, 07:09:40 PM »
Good idea :thumbsup:

What I find strange is that C/C++ are not able to choose the format automagically, as in print MyReal8. In MASM, you can easily write a macro that checks whether the argument is an integer, a QWORD, a REAL8 etc - see attachment (pure Masm32 SDK) :cool:

PrintC is in efiutil.inc
« Last Edit: June 22, 2022, 02:42:09 AM by jj2007 »

TimoVJL

  • Member
  • *****
  • Posts: 1233
Re: Macro version of printf
« Reply #2 on: June 22, 2022, 03:53:09 AM »
What I find strange is that C/C++ are not able to choose the format automagically,
C++ have classes for that, C don't.
But some C compilers have extension __typeof
« Last Edit: June 22, 2022, 05:03:21 PM by TimoVJL »
May the source be with you

HSE

  • Member
  • *****
  • Posts: 2253
  • AMD 7-32 / i3 10-64
Re: Macro version of printf
« Reply #3 on: June 22, 2022, 08:00:31 AM »
Hi Biterider!

The idea is to find a similar way, sort of like HSE did in its PrintC macro.

 :thumbsup: That macro is a very elemental FSM wich read a character code between "%" and space. To emulate C' prints FSM must be more complex and read several characters code between [% or \ ] and [%, \,space, "," , ";" or "."] (at least).

Regards, HSE.
Equations in Assembly: SmplMath

HSE

  • Member
  • *****
  • Posts: 2253
  • AMD 7-32 / i3 10-64
Re: Macro version of printf
« Reply #4 on: July 31, 2022, 07:19:37 AM »
Hi all!

Following Biterider challenge, this is a far better approach than in my previous macro:

Code: [Select]
PrintH1 macro mensaje, args:VARARG
    local pos1
   
    ifndef crlf1$
        CStr crlf1$ , 13, 10, 0
    endif
    ifndef cr1$
        CStr cr1$, 13, 0
    endif
    ifndef lf1$
        CStr lf1$, 10, 0
    endif
    ifndef tab1$
        CStr tab1$, 9, 0
    endif

    ifndef UEFI_Print_Message
        UEFI_Print_Message = 0
    else
        UEFI_Print_Message = UEFI_Print_Message + 1
    endif
    ifndef UEFI_Print_buffer1
      .data
            UEFI_Print_buffer1 CHR 1024 dup (0)
            UEFI_Print_buffer2 CHR  248 dup (0)
            UEFI_Print_buffer3 CHR  4 dup (0)

            CStr UEFI_Error_0, "EFI_SUCCESS"
            CStr UEFI_Error_1, "EFI_LOAD_ERROR"
            CStr UEFI_Error_2, "EFI_INVALID_PARAMETER"
            CStr UEFI_Error_3, "EFI_UNSUPPORTED"
            CStr UEFI_Error_4, "EFI_BAD_BUFFER_SIZE"
            CStr UEFI_Error_5, "EFI_BUFFER_TOO_SMALL"
            CStr UEFI_Error_6, "EFI_NOT_READY"
            CStr UEFI_Error_7, "EFI_DEVICE_ERROR"
            CStr UEFI_Error_8, "EFI_WRITE_PROTECTED"
            CStr UEFI_Error_9, "EFI_OUT_OF_RESOURCES"
            CStr UEFI_Error_10, "EFI_VOLUME_CORRUPTED"
            CStr UEFI_Error_11, "EFI_VOLUME_FULL"
            CStr UEFI_Error_12, "EFI_NO_MEDIA"
            CStr UEFI_Error_13, "EFI_MEDIA_CHANGED"
            CStr UEFI_Error_14, "EFI_NOT_FOUND"
            CStr UEFI_Error_15, "EFI_ACCESS_DENIED"
            CStr UEFI_Error_16, "EFI_NO_RESPONSE"
            CStr UEFI_Error_17, "EFI_NO_MAPPING"
            CStr UEFI_Error_18, "EFI_TIMEOUT"
            CStr UEFI_Error_19, "EFI_NOT_STARTED"
            CStr UEFI_Error_20, "EFI_ALREADY_STARTED"
            CStr UEFI_Error_21, "EFI_ABORTED"
            CStr UEFI_Error_22, "EFI_ICMP_ERROR"
            CStr UEFI_Error_23, "EFI_TFTP_ERROR"
            CStr UEFI_Error_24, "EFI_PROTOCOL_ERROR"

            CStr UEFI_File_Error_0, "EFI_SUCCESS" ;"The file was opened."
            CStr UEFI_File_Error_1, "EFI_NOT_FOUND" ;"The specified file could not be found on the device."
            CStr UEFI_File_Error_2, "EFI_NO_MEDIA" ;"The device has no medium."
            CStr UEFI_File_Error_3, "EFI_MEDIA_CHANGED" ;"The device has a different medium in it or the medium is no longer supported."
            CStr UEFI_File_Error_4, "EFI_DEVICE_ERROR" ;"The device reported an error."
            CStr UEFI_File_Error_5, "EFI_VOLUME_CORRUPTED" ;"The file system structures are corrupted."
            CStr UEFI_File_Error_6, "EFI_WRITE_PROTECTED" ;"An attempt was made to create a file, or open a file for write when the media is write-protected."
            CStr UEFI_File_Error_7, "EFI_ACCESS_DENIED" ;"The service denied access to the file."
            CStr UEFI_File_Error_8, "EFI_OUT_OF_RESOURCES" ;"Not enough resources were available to open the file."
            CStr UEFI_File_Error_9, "EFI_VOLUME_FULL" ;"The volume is full."
            if 0
            cuenta = 1
            EFI_ERROR$ dq offset UEFI_Error_0
            REPEAT 24
             %   dq offset @CatStr(<UEFI_Error_>,%cuenta)
                cuenta = cuenta + 1
            ENDM
            cuenta = 1
            EFI_FILE_ERROR$ dq offset UEFI_File_Error_0
            REPEAT 9
             %   dq offset @CatStr(<UEFI_File_Error_>,%cuenta)
                cuenta = cuenta + 1
            ENDM
            endif
    endif

    .code

    freg_pushad
    cuenta = 0
    FOR arg,<args>
@CatStr(<argumento_>,%cuenta) textequ <arg>
       cuenta = cuenta + 1
    ENDM
    mov rax, 0
    mov CHR ptr UEFI_Print_buffer1, ax

    cuenta = 0
    pos1 = 0
    parte = 0
    contenido TEXTEQU <>
    tipoarg TEXTEQU <>

    abrecomilla = 0
    hayslack = 0
    haypercent = 0
    abierto = 0
    FORC char,<&mensaje>
        tk_1 INSTR 1,<">,<&char>
        if tk_1 EQ 0
            if (abrecomilla eq 0) or (abrecomilla eq 2)
                .err Problem with string delimitation
            endif
           
            tk_2a INSTR 1,<\>,<&char>
            if tk_2a
                if hayslack
                    hayslack = 0
                contenido CATSTR contenido,<!!!\>
                    goto continue
                else
                    hayslack = 1
                    goto continue
                endif         
            endif
            tk_2b INSTR 1,<%>,<&char>
            if tk_2b
                if hayslack
                    hayslack = 0
                elseif haypercent
                    .err Not allowed percent sucesive symbols   
                else
                    haypercent = 1
                    goto continue
                endif         
            endif
           
            tk_3a INSTR 1, <rnt>, <&char>
            if tk_3a
                if hayslack
                if abierto
                    abierto = 0
                    @CatStr(<CStrW UEFI_Print_Message_>,%UEFI_Print_Message,<_>,%parte,<, !">,%contenido,<!">)
                    invoke StrCat, addr UEFI_Print_buffer1, addr @CatStr(<UEFI_Print_Message_>,%UEFI_Print_Message,<_>,%parte)
                    contenido TEXTEQU <>
                    parte = parte + 1
                endif
                tk_3a1 INSTR 1, <r>, <&char>
                if tk_3a1
                    invoke StrCat, addr UEFI_Print_buffer1, addr cr1$
                endif
                tk_3a2 INSTR 1, <n>, <&char>
                if tk_3a2
                    invoke StrCat, addr UEFI_Print_buffer1, addr lf1$
                endif
                tk_3a3 INSTR 1, <t>, <&char>
                if tk_3a3
                    invoke StrCat, addr UEFI_Print_buffer1, addr tab1$
                endif
                hayslack = 0
                goto continue
            endif
            endif
           
            tk_3b INSTR 1, <scdihfgrq>, <&char>
            if tk_3b
                if haypercent
                if abierto
                    abierto = 0
                    @CatStr(<CStrW UEFI_Print_Message_>,%UEFI_Print_Message,<_>,%parte,<, !">,%contenido,<!">)
                    invoke StrCat, addr UEFI_Print_buffer1, addr @CatStr(<UEFI_Print_Message_>,%UEFI_Print_Message,<_>,%parte)
                    contenido TEXTEQU <>
                    parte = parte + 1
                endif
                tk_3b1 INSTR 1, <s>, <&char>
                if tk_3b1
                        invoke StrCat, addr UEFI_Print_buffer1, @CatStr(<argumento_>,%cuenta)
                endif
                tk_3b1 INSTR 1, <c>, <&char>
                if tk_3b1
                    mov ax, @CatStr(<argumento_>,%cuenta)
                    mov UEFI_Print_buffer3, ax
                    invoke StrCat, addr UEFI_Print_buffer1, addr UEFI_Print_buffer3
                    endif
                tk_3b1 INSTR 1, <d>, <&char>
                if tk_3b1
                        invoke uqword2decW, addr UEFI_Print_buffer2, @CatStr(<argumento_>,%cuenta)
                        invoke StrCat, addr UEFI_Print_buffer1, addr UEFI_Print_buffer2
                    endif
                tk_3b1 INSTR 1, <i>, <&char>
                if tk_3b1
                    invoke udword2decW, addr UEFI_Print_buffer2, @CatStr(<argumento_>,%cuenta)
                    invoke StrCat, addr UEFI_Print_buffer1, addr UEFI_Print_buffer2
                    endif
                tk_3b1 INSTR 1, <h>, <&char>
                if tk_3b1
                    invoke qword2hexW, addr UEFI_Print_buffer2, @CatStr(<argumento_>,%cuenta)
                    invoke StrCat, addr UEFI_Print_buffer1, addr UEFI_Print_buffer2
                    endif
                tk_3b1 INSTR 1, <f>, <&char>
                if tk_3b1
                    fld @CatStr(<argumento_>,%cuenta) 
                    invoke St0ToStr, addr UEFI_Print_buffer2, 8, 4, 0
                    fstp st
                    invoke StrCat, addr UEFI_Print_buffer1, addr UEFI_Print_buffer2
                endif
                tk_3b1 INSTR 1, <g>, <&char>
                if tk_3b1
                    freg_pushad
                    invoke GUID2StrW, addr UEFI_Print_buffer2, @CatStr(<argumento_>,%cuenta)
                    invoke StrCat, addr UEFI_Print_buffer1, addr UEFI_Print_buffer2
                    freg_popad
                endif
                tk_3b1 INSTR 1, <r>, <&char>
                if tk_3b1
                    cuento = 1
                    %mov rax, @CatStr(<argumento_>,%cuenta)
                    and rax, 0FFh
                    .if rax == 0
                       lea rdx, @CatStr(<UEFI_Error_>, 0)
                    repeat 24
                   % .elseif rax == cuento
                       lea rdx, @CatStr(<UEFI_Error_>, %cuento)
                       cuento = cuento + 1
                    endm
                    .endif
                    invoke StrCat, addr UEFI_Print_buffer1, rdx
                    endif
                tk_3b1 INSTR 1, <q>, <&char>
                if tk_3b1
                    cuento = 1
                    %mov rax, @CatStr(<argumento_>,%cuenta)
                    and rax, 0FFh
                    .if rax == 0
                       %lea rdx, @CatStr(<UEFI_File_Error_>, 0)
                    repeat 9
                   % .elseif rax == cuento
                       %lea rdx, @CatStr(<UEFI_File_Error_>, %cuento)
                       cuento = cuento + 1
                    endm
                    .endif
                    invoke StrCat, addr UEFI_Print_buffer1, rdx
                    endif
                    cuenta = cuenta + 1
                haypercent = 0
                goto continue
                endif 
            endif
           
            abierto = 1
        else
            if abrecomilla EQ 1
                if abierto
                    @CatStr(<CStrW UEFI_Print_Message_>,%UEFI_Print_Message,<_>,%parte,<, !">,%contenido,<!">)
                    invoke StrCat, addr UEFI_Print_buffer1, addr @CatStr(<UEFI_Print_Message_>,%UEFI_Print_Message,<_>,%parte)
                    contenido TEXTEQU <>
                endif
                abrecomilla = 2
                abierto = 0
            else
                abrecomilla = 1
            endif
        endif
        if abierto
            contenido CATSTR contenido,<&char>
        endif
:continue           
    ENDM

    mov xcx, pConsoleOut                                     
    invoke [xcx].ConOut.OutputString, xcx, ADDR UEFI_Print_buffer1
   
  freg_popad 
endm

Escapes are:
  \\ ="\"
  \% = "%"
  \r = begin of line
  \n = new line
  \t = tab

Variables:
  %s = string by addr
  %c = character
  %h =hexagesimal
  %i  = decimal dword
  %d = decimal qword
  %f = floating point (real4 or real8)
  %g = GUID value
  %r = EFI error
  %q = EFI file error

then:
Code: [Select]
    PrintH " %i %i %i - %i:%i:%i.%i ", [xbx].EFI_TIME.Month, [xbx].EFI_TIME.Day, [xbx].EFI_TIME.Year, [xbx].EFI_TIME.Hour, [xbx].EFI_TIME.Minute, [xbx].EFI_TIME.Second, [xbx].EFI_TIME.Nanosecond ; GNU-EFI apparently has a print function for time... Oh well.
show:
Code: [Select]
7 30 2022 - 20:57:40.0
Code: [Select]
  PrintH "SetAttribute \\\%  %r \n\n", Statusshow:
Code: [Select]
SetAttribute \%  EFI_SUCCESS


Obvioulsly, more tests are needed  :biggrin:

Regards, HSE
Equations in Assembly: SmplMath

Biterider

  • Moderator
  • Member
  • *****
  • Posts: 1041
  • ObjAsm Developer
    • ObjAsm
Re: Macro version of printf
« Reply #5 on: July 31, 2022, 07:31:43 AM »
Hi
I finally have some time to experiment a bit with this idea.
It wasn't as hard as I expected as most of the parts already existed.

The macro I call PrintF supports format specifiers used to interpret the passed varargs, as well as escape sequences to use non-printable characters or MASM special characters.

The documentation as well as the code can be found in the attached file.

ObjAsm users will find the macro in the System.inc file.

Example:   
Code: [Select]
lea xdi, cBuffer
mov CHR ptr [xdi], 0
mov xsi, 12345678h
PrintF xdi, 'The content at memory location ¦HXh is ¦F3 ml ¦ST', xsi, $CReal4(1.23), $OfsCStr("(current reading)")
DbgStr cBuffer

lea xdi, cBuffer
PrintF xdi, "Memory at \[¦HXh\]: ¦EB", xsi, $CReal8(1.23456789123456789)
DbgStr cBuffer

Result:
Code: [Select]
cBuffer = The content at memory location 12345678h is 1.230 ml (current reading)
cBuffer = Memory at <12345678h>: 1.23456789123E+0000

The code generated runs much faster than a printf call because it is tailored by the macro for this specific case.
In this case, the code for ANSI characters in 64-bit looks like this:
Code: [Select]
lea          rdi,[cBuffer] 
 mov         byte ptr [rdi],0 
 mov         rsi,12345678h 
 mov         dword ptr [rdi],20656854h 
 mov         dword ptr [rdi+4],746E6F63h 
 mov         dword ptr [rdi+8],20746E65h 
 mov         dword ptr [rdi+0Ch],6D207461h 
 mov         dword ptr [rdi+10h],726F6D65h 
 mov         dword ptr [rdi+14h],6F6C2079h 
 mov         dword ptr [rdi+18h],69746163h 
 mov         word ptr [rdi+1Ch],6E6Fh 
 mov         byte ptr [rdi+1Eh],20h 
 add         rdi,1Fh 
 mov         rcx,rdi 
 mov         rdx,rsi 
 call        qword2hexA (07FF79E371880h) 
 add         rdi,10h 
 mov         dword ptr [rdi],73692068h 
 mov         byte ptr [rdi+4],20h 
 add         rdi,5 
 fld         dword ptr [CR4_P1p23 (07FF79E373728h)] 
 mov         rcx,rdi 
 xor         edx,edx 
 mov         r8d,3 
 xor         r9d,r9d 
 call        St0ToStrA (07FF79E3714B0h) 
 fstp        st(0) 
 mov         rcx,rdi 
 call        StrLengthA (07FF79E371460h) 
 lea         rdi,[rdi+rax] 
 mov         dword ptr [rdi],206C6D20h 
 add         rdi,4 
 mov         rcx,rdi 
 mov         rdx,7FF79E373730h 
 call        StrCopyA (07FF79E371480h) 
 mov         rcx,rdi 
 call        StrLengthA (07FF79E371460h) 
 lea         rdi,[rdi+rax] 
 mov         byte ptr [rdi],0 

Biterider

Biterider

  • Moderator
  • Member
  • *****
  • Posts: 1041
  • ObjAsm Developer
    • ObjAsm
Re: Macro version of printf
« Reply #6 on: July 31, 2022, 07:35:29 AM »
Hi HSE
Funny that we did something similar  :thumbsup:
I'll check your version.

Biterider

HSE

  • Member
  • *****
  • Posts: 2253
  • AMD 7-32 / i3 10-64
Re: Macro version of printf
« Reply #7 on: July 31, 2022, 07:56:42 AM »
Hi Biterider!!

Fantastic! That is more powerfull for general use  :thumbsup:.

I follow more close GNU-EFI Print function syntax, because I need to replace that to translate some UEFI examples.

HSE
Equations in Assembly: SmplMath

Biterider

  • Moderator
  • Member
  • *****
  • Posts: 1041
  • ObjAsm Developer
    • ObjAsm
Re: Macro version of printf
« Reply #8 on: July 31, 2022, 04:50:46 PM »
Hi HSE
I'll borrow the GUID representation idea from your implementation. I did not think about that.
Funny that you also thought about implementing escape sequences :thumbsup:

If we adopt the PrintH1 macro, will it change the previous UEFI demos?
Perhaps an additional feature could be changing foreground and/or background colors using an escape sequence for each.

Biterider

jj2007

  • Member
  • *****
  • Posts: 13311
  • Assembly is fun ;-)
    • MasmBasic
Re: Macro version of printf
« Reply #9 on: July 31, 2022, 09:38:50 PM »
Check this YouTube video (posted by Jack).

Quote
When The Motherboard Comes With a Virus
60,224 views  Jul 31, 2022  A UEFI rootkit by the name of cosmicstrand has been detected on several motherboards (images analyzed in the security writeup came from Asus and Gigabyte H81 motherboards) that has the capability to tamper with Windows operating systems installed to any disk, and is persistent upon os resets or hard drive changes.

HSE

  • Member
  • *****
  • Posts: 2253
  • AMD 7-32 / i3 10-64
Re: Macro version of printf
« Reply #10 on: August 01, 2022, 05:08:26 AM »
I'll borrow the GUID representation idea from your implementation. I did not think about that.

 :biggrin:  :thumbsup:

If we adopt the PrintH1 macro, will it change the previous UEFI demos?

In some error messages calling PrintC there are originals "\n", so in that cases an additional line will be added (because PrintC call PrintLn). Anyway, they are 6 lines in Paoloni2010.inc  :biggrin:
Equations in Assembly: SmplMath

Biterider

  • Moderator
  • Member
  • *****
  • Posts: 1041
  • ObjAsm Developer
    • ObjAsm
Re: Macro version of printf
« Reply #11 on: August 03, 2022, 05:54:05 AM »
Hi
Unfortunately I found a macro in the masm32 macros that is also called printf. To avoid conflicts, I renamed the new version to WriteF.
On the bright side, I could easily add a new debug macro now called DbgWriteF that simply outputs the result of WriteF to any debug device.

Example from a recently discussed case
Code: [Select]
DbgWriteF DBG_COLOR_TEXT, "HtmlHelp Constants", "¦ST¦AT¦ST", pName, 35, pValue
Biterider