8
votes

I am using Borland Turbo C++ with some inlined assembler code, so presumably Turbo Assembler (TASM) style assembly code. I wish to do the following:

void foo::bar( void )
{
    __asm
    {
      mov eax, SomeLabel
      // ...
    }
    // ...
SomeLabel:
    // ...
}

So the address of SomeLabel is placed into EAX. This doesn't work and the compiler complains of: Undefined symbol 'SomeLabel'.

In Microsoft Assembler (MASM) the dollar symbol ($) serves as the current location counter, which would be useful for my purpose. But again this does not seem to work in Borlands Assember (expression syntax error).

Update: To be a little more specific, I need the compiler to generate the address it moves into eax as a constant during compilation/linking and not at run time, so it will compile like "mov eax, 0x00401234".

Can anybody suggest how to get this working?

UPDATE: To respond to Pax's question (see comment), If the base address is changed at run time by the Windows loader the DLL/EXE PE image will still be relocated by the Windows loader and the labels address will be patched at run time by the loader to use the re-based address so using a compile/link time value for the label address is not an issue.

Many thanks in advance.

12
Does this work if you define SomeLabel inside the asm block?user57368
for gcc, getting a labels address is "&&label", have you tried it? maybe it works for borland too?Johannes Schaub - litb
How exactly do you intend to get the value at compile time? The loader may change (at load time, long after linking) the address at which this code is loaded, making your compile/link value useless.paxdiablo
Perhaps you need to restate the question: what EXACTLY are you going to do with eax once it's loaded.paxdiablo
paxdiablo: The Windows PE loader will relocate an exe and process the PE files .reloc section, updating any fixed address with the newly loaded base address so EAX would always be correct. Also it shouldnt matter what I am going to do with EAX once it is loaded, the question is how to set EAX to the address of SomeLabel (which is easily possible in MSVC just not C++ Builder).QAZ

12 Answers

4
votes

Last time I tried to make some assembly code Borland-compatible I came across the limitation that you can't forward-reference labels. Not sure if that's what you're running into here.

4
votes

Everything I can find about Borland suggests this ought to work. Similar questions on other sites (here and here) suggest that Borland can handle forward-references for labels, but insists on labels being outside asm blocks. However, as your label was already outside the asm block...

I am curious whether your compiler would allow you to use this label within, for instance, a jmp instruction. When toying around with it (admittedly, on a completely different compiler), I found a pesky tendency for the compiler to complain about operand types.

The syntax is quite different, and it's my first attempt at inline asm in a long time, but I believe I've munged this enough to work under gcc. Perhaps, despite the differences, this might be of some use to you:

#include <stdio.h>
int main()
{
    void *too = &&SomeLabel;
    unsigned int out;
    asm
    (
      "movl %0, %%eax;"
      :"=a"(out)
      :"r"(&&SomeLabel)
    );
SomeLabel:
    printf("Result: %p %x\n", too, out);

    return 0;
}

This generates:

...
        movl    $.L2, %eax
...
.L2:

The && operator is a non-standard extension, I wouldn't expect it to work anywhere other than gcc. Hopefully this may have stirred up some new ideas... Good luck!

Edit: Though it's listed as Microsoft specific, here is another instance of jumping to labels.

1
votes

3 suggestions:

1) put a '_' in front of the SomeLabel in the assembly so it becomes "mov eax, _SomeLabel ". Usually the compiler will add one when it translates C into assembly.

Or

2) put the label in an assembly section. This will prevent the compiler from adding the '_'.

Or

3) comment out the assembly, compile, and look in the listing file (*.lst) to see what the label name becomes.

1
votes

Does the Turbo C++ environment have a way to set options for TASM (I know that some of the Borland IDEs did)?

If so, see if changing the option for "Maximum passes (/m)" to 2 or more helps (it might default to 1 pass).

Also, if you're using a long label name that might pose a problem - at least one IDE had the default set to 12. Change the "Maximum symbol length (/mv) option".

This information is based on Borland's RAD Studio IDE:

1
votes

A couple more things (shots in the dark) to try:

  • see if using the following assembly instruction helps:

    mov eax, offset SomeLabel
    
  • most compilers can produce an assembly listing of the code they generate (not sure if Turbo C++ can, since Codegear/Embarcadero position it as a free, non-professional compiler).

    Try producing a listing with C code that has an uses a label (as a goto target for example), with some inline assembly in the same function - but don't try to access the label from the assembly. This is so you can get a compiler with no errors and an assembly listing. Something like:

    int foo()
    {
        int x = 3;
        printf( "x =%d\n", x);
        goto SomeLabel;
                               //
        __asm {
            mov eax, 0x01
        }
                               //
    SomeLabel:
        printf( "x =%d\n", x);
                               //
        return x;
    }
    

    Look at the assembly listing and see if the generated assembly decorates the label name in a way that you might be able to replicate in the inline assembly.

0
votes

From what I recall, you can't use an external (C++) label in your inline assembly, although you can have TASM-style labels in the asm block that can be referenced by the assembly instructions itself. I think I would use a flag and a post-assembler switch statement to handle branching. For example:

int result=0;

__asm__ {
    mov result, 1
}

switch (result){
    case 1:  printf("You wanted case 1 to happen in your assembler\n"); break;
    case 0:  printf("Nothing changed with the result variable.. defaulting to:\n");
    default: printf("Default case!\n"); break;
}
0
votes

I don't know about your compiler / assembler specifically, but a trick I've used quite a bit is to call the next location and then pop the stack into your register. Be certain the call you make only pushes the return address.

0
votes

I think the problem you're running into is that a label inside the __asm block and the label in the C++ code are two completely different things. I wouldn't expect that you could reference a C++ label in that way from inline assembly, but I must say it's been a very long time since I've used Turbo C++.

Have you tried the lea instruction instead of mov?

0
votes

Just guessing since I haven't used inline assembler with any C/++ compiler...

void foo::bar( void )
{
    __asm
    {
      mov eax, SomeLabel
      // ...
    }
    // ...
    __asm
    {
      SomeLabel:
      // ...
    }
    // ...
}

I don't know the exact syntax of TASM.

0
votes

This is a variant of Ivan's suggestion but give this a try:

void foo::bar( void )
{
    __asm
    {
      mov eax, offset SomeLabel
      // ...
    }
    // ...
    __asm SomeLabel:
    // ...
}
0
votes

Here's a possible method:

// get_address
// gets the address of the instruction following the call
// to this function, for example
//     int addr = get_address (); // effectively returns the address of 'label'
//   label:
int get_address ()
{
    int address;
    asm
    {
        mov eax,[esp+8]
        mov address,eax
    }
    return address;
}
// get_label_address
// a bit like get_address but returns the address of the instruction pointed
// to by the jmp instruction after the call to this function, for example:
//     int addr;
//     asm
//     {
//       call get_label_address // gets the address of 'label'
//       jmp label
//       mov addr,eax
//     }
//     <some code>
//   label:
// note that the function should only be called from within an asm block.
int get_label_address()
{
    int address = 0;
    asm
    {
        mov esi,[esp+12]
        mov al,[esi]
        cmp al,0ebh
        jne not_short
        movsx eax,byte ptr [esi+1]
        lea eax,[eax+esi-1]
        mov address,eax
        add esi,2
        mov [esp+12],esi
        jmp done
    not_short:
        cmp al,0e9h
        jne not_long
        mov eax,dword ptr [esi+1]
        lea eax,[eax+esi+2]
        mov address,eax
        add esi,5
        mov [esp+12],esi
        jmp done
    not_long:
        // handle other jmp forms or generate an error
    done:
    }
    return address;
}
int main(int argc, char* argv[])
{
    int addr1,addr2;
    asm
    {
        call get_label_address
        jmp Label1
        mov addr1,eax
    }

    addr2 = get_address ();
Label1:
    return 0;
}

It's a bit hacky but it works in the version of Turbo C++ that I have. It almost certainly is dependant on the compiler and optimisation settings.

0
votes

one of the options would be to use separate "naked" (prolog-less) procedure SomeLabel instead of label