1

I'm using Visual Studio 2017 Community to build a test console C++ application. I need to include an assembly function into that project:

extern "C" void* __fastcall getBaseFS(void);

To include an asm file I right-clicked the project and went to "Build dependencies" -> "Build Customization" and checked "masm" in the list there.

I can then add an asm file by right-clicking my project -> Add New item -> and then add "asm_x64.asm" file where I write my x86-64 asm code:

.code

getBaseFS PROC

mov ecx, 0C0000100H   ; IA32_FS_BASE
rdmsr

shl rdx, 32
or rax, rdx

ret

getBaseFS ENDP

END

This works in a 64-bit project.

The problem is that when I switch solution platform from x64 to x86:

enter image description here

my asm file needs to change. So in a sense I need to include a different "asm_x86.asm" file into compilation that is used only for x86 builds vs. x64 builds.

What's the best way to automate this switch?

c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • 1
    Windows applications can't use the RDMSR instruction as it's privileged instruction. Also the IA32_FS_BASE MSR only exists on 64-bit CPUs. – Ross Ridge Jul 06 '18 at 21:34
  • @RossRidge: OK. But this is not what I'm asking here. – c00000fd Jul 06 '18 at 22:03
  • 1
    So your generic question is how to make the build system select a `.asm` file for the target architecture. You should probably spend less time on that specific example, or just make one up (e.g. a different version of the same function taking advantage of more registers). A macro within one file would be an alternative way to get this functionality, assuming MASM lets you detect the current BITS mode. (NASM does with `%if __BITS__ == 32` / `%else` / `%endif`). – Peter Cordes Jul 06 '18 at 23:02
  • If it's just a couple of lines of code, a #define is probably your best bet. But if you need something more involved, VS's Configuration Manager allows you to specify which projects to build for which platforms (clear the 'Build' checkbox for the one you don't want it to build). So, have a project for x86 asm, and another for x64 asm. – David Wohlferd Jul 07 '18 at 00:34
  • @PeterCordes: Guys, I haven't really done assembly for a while now. But I don't think `%if` is a way to write preprocessor directives in masm. Possibly just `IF __BITS__ == 64`, but it fails too with `error A2008: syntax error` error. Also I can't find any info on `__BITS__` in masm either. – c00000fd Jul 07 '18 at 01:48
  • @c00000fd: I said that was *NASM* syntax, not MASM, to give an example of a different assembler where it is possible. I have no idea what MASM's macro syntax is, or how you'd detect the mode or bitness. But that's something you could google on if a one-file solution would work for you. – Peter Cordes Jul 07 '18 at 01:54
  • @PeterCordes: Well, `NASM` for Visual Studio is like Clippy for Vim. PS. Thanks for letting me Google it. – c00000fd Jul 07 '18 at 02:02
  • 3
    You might want to try this trick https://stackoverflow.com/a/9155436/3857942 – Michael Petch Jul 07 '18 at 02:05
  • I wasn't suggesting you actually *use* NASM, I'm just saying "here's a feature MASM might have, and here's how a different assembler chose to do it". I literally have no idea where to start looking for such a feature in MASM; I don't even use it except some vague familiarity from reading/writing SO answers, so google for `masm detect mode` is where I'd start looking. Sorry if you'd rather I just didn't say anything and waited for someone who knows more about MASM to comment. – Peter Cordes Jul 07 '18 at 02:05
  • @PeterCordes: OK, sorry. I was just frustrated with this seemingly simple task. My bad. – c00000fd Jul 07 '18 at 02:14
  • Do you have "Pre-Build" option in your Project/Build properties? Few years ago I had the same problem with ASM file and .NET and I solved it using a copy command based on CPU mode. I mean, there is a variable that informs it in VS and, in a condition there, I copied x86 or x64 files to a fixed-filename (eg. BINcode) to be attached into project. It's a dirty but effective way. The project were *always* looking for a file called "BINcode.dll"; was its contents that changed in pre-build via copy. – David BS Jul 07 '18 at 02:37
  • 1
    @DavidBS: It may work like that. IDK. I am not a big fan of pre-build scripts and other configuration stuff. It all has a tendency of either being reset for no reason or lost when solutions move around. I think I got it solved though with preprocessor definitions in one asm file. – c00000fd Jul 07 '18 at 03:54

2 Answers2

2

OK, thanks to Michael Petch, I got it solved. Had to put both x64 and x86 code in one .asm file.

(There's another proposed option to deal with build configuration, but I prefer the method I'm showing here. I had a bad luck with those build configurations disappearing when solutions were moved from computer to computer.)

So, I'm not sure why using IFDEF RAX works, and Microsoft's own proposed ifndef X64 doesn't. But oh well. If anyone knows, please post a comment.

asm_code.asm file:

IFDEF RAX
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; x64 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; WinAPI to call
extrn Beep : proc


.data

align   8
beep_freq:
    dq  700 ; hz
beep_dur:
    dq  200 ; ms
str_from:
    db  "Hail from x64 asm", 0



.code

useless_sh_t_function__get_GS_a_string_and_beep PROC
    ; parameter = CHAR** for a string pointer
    ; return = value of GS register selector

    mov     rax, str_from
    mov     [rcx], rax

    mov     rdx, qword ptr [beep_dur]
    mov     rcx, qword ptr [beep_freq]
    call    Beep

    mov     rax, gs
    ret
useless_sh_t_function__get_GS_a_string_and_beep ENDP




ELSE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; x86 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.686p  
.XMM  
.model flat, C  


.data

align   4
beep_freq   dd  700 ; hz
beep_dur    dd  200 ; ms
str_from    db  "Hail from x86 asm", 0


.code

; WinAPI to call
extrn stdcall Beep@8 : proc


useless_sh_t_function__get_GS_a_string_and_beep PROC
    ; parameter = CHAR** for a string pointer
    ; return = value of GS register selector

    mov     eax, [esp + 4]
    mov     [eax], OFFSET str_from

    push    dword ptr [beep_dur]
    push    dword ptr [beep_freq]
    call    Beep@8

    mov     eax, gs
    ret
useless_sh_t_function__get_GS_a_string_and_beep ENDP


ENDIF

END

main.cpp file:

#include "stdafx.h"
#include <Windows.h>

extern "C" {
    size_t useless_sh_t_function__get_GS_a_string_and_beep(const CHAR** ppString);
};

int main()
{
    const char* pString = NULL;
    size_t nGS = useless_sh_t_function__get_GS_a_string_and_beep(&pString);
    printf("gs=0x%Ix, %s\n", nGS, pString);

    return 0;
}
c00000fd
  • 20,994
  • 29
  • 177
  • 400
  • The Microsoft solution will work but you have to modify your masm (ml64) build parameters to pass `-DX64` command line option. – Michael Petch Jul 07 '18 at 04:05
  • I'd assume `IFDEF RAX` works because the parser uses a unified table of keywords, macros, and register names (and maybe symbols/labels). But it's not a register name in 32-bit, so unless you define it as a symbol (which you *could* in 32-bit code) or a macro, it's just another undefined identifier. Anyway, that's what I can conclude from the fact that it does work. If it's officially supported, then that implementation detail is guaranteed to keep working. – Peter Cordes Jul 07 '18 at 04:15
2

Good that you found a way to handle your use case.

However, if you have more asm files, or only need them for some build types, you could also change the settings for each individual file for the parts where it differs from the project defaults.

Just right-click a file name to get to its individual properties.

To have an asm file included in x64 builds only, you can use these settings:

enter image description here

And then exclude it from 32-bit builds:

enter image description here

These settings are available for all file type, not only for .asm files.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203