3

For a task at my school, I need to write a C Program which does a 16 bit addition using an assembly programm. Besides the result the EFLAGS shall also be returned.

Here is my C Program:

int add(int a, int b, unsigned short* flags);//function must be used this way
int main(void){
    unsigned short* flags = NULL;
    printf("%d\n",add(30000, 36000, flags);// printing just the result
    return 0;
}

For the moment the Program just prints out the result and not the flags because I am unable to get them.

Here is the assembly program for which I use NASM:

global add
section .text

add:
    push ebp    
    mov ebp,esp
    mov ax,[ebp+8]
    mov bx,[ebp+12]
    add ax,bx
    mov esp,ebp
    pop ebp
    ret

Now this all works smoothly. But I have no idea how to get the pointer which must be at [ebp+16] pointing to the flag register. The professor said we will have to use the pushfd command.

My problem just lies in the assembly code. I will modify the C Program to give out the flags after I get the solution for the flags.

rkhb
  • 14,159
  • 7
  • 32
  • 60
  • 1
    Partial answer: you need to call the `add`functikon like this: `unsigned short flags; printf("%d\n",add(30000, 36000, &flags));` otherwise you'll never get the flags. In the `add` function after the `aff ax,bx` instruction: `mov ebx,[ebp+16] ; ebx points to the flags pointzer provided by the caller`. Now you must put `mov eflags,[ebx]` but not sure how actually get the eflags register. – Jabberwocky Sep 13 '16 at 13:12
  • Hey thanks for the ideas! But as I stated the parameters of the add function can not be changed! – Helga Schmid Sep 13 '16 at 14:25
  • My suggestions doesn't change to parameters of the `add` function. You are still passing an `unsigned short *` to the add function. `&flags` is a pointer to the `flags` variable. This allows the `add`function to change the content of the `flags` variable. – Jabberwocky Sep 13 '16 at 14:46
  • 1
    Has your professor discussed calling conventions and volatile and non-volatile registers? Is this for 32-bit Linux? If so, your code clobbers the _EBX_ register, but you don't preserve its value. I'd use volatile register _ECX_ rather than _EBX_. _EAX_, _ECX_, and _EDX_ are volatile registers so don't need to be preserved by the function itself. As well the function returns a 32-bit integer (`int`) according to the prototype for `add`. Unfortunately you never set the upper 16-bits of _EAX_ (return value)so they could have garbage in those bits causing wrong result – Michael Petch Sep 13 '16 at 15:20
  • You may think your code is working but it does have subtle bugs. – Michael Petch Sep 13 '16 at 15:22
  • No we had nothing about volatile and non volatile registers. Yes I am using Debian GNU/Linux 8. But apparently the results are correct so far. – Helga Schmid Sep 13 '16 at 15:39
  • Why do you even want to copy FLAGS into an integer variable instead of just using a debugger to look at it? – Peter Cordes Sep 13 '16 at 15:39
  • "correct so far" doesn't mean bug-free in assembly! As soon as you call this function from a caller that assumes EBX will be preserved, you will have a problem. The ABI/calling convention says callers can assume that EBX isn't modified. It works when you call it from a function that happens not to break when you destroy EBX – Peter Cordes Sep 13 '16 at 15:42
  • if you call the c program it should give out something like that: 32769+2=32771 Flags: O D I T S Z A P C 0 0 1 0 1 0 0 0 0 1 1 0 – Helga Schmid Sep 13 '16 at 15:45
  • 3
    Do not edit away the content of your question. That's vandalism. Once you've posted a question and it has answers, it doesn't belong just to you, it belongs to the community. The question is necessary to find and understand the answers. By taking away the question, it you're vandalizing the answers as well. – Gilles 'SO- stop being evil' Nov 20 '16 at 13:09
  • 1
    If there are copyright problems that require this question to be removed, a DMCA takedown needs to be filed. https://stackexchange.com/legal/terms-of-service#15CopyrightPolicy – Ry- Nov 20 '16 at 14:23

2 Answers2

5

Normally you'd just use a debugger to look at flags, instead of writing all the code to get them into a C variable for a debug-print. Especially since decent debuggers decode the condition flags symbolically for you, instead of or as well as showing a hex value.

You don't have to know or care which bit in FLAGS is CF and which is ZF. (This information isn't relevant for writing real programs, either. I don't have it memorized, I just know which flags are tested by different conditions like jae or jl. Of course, it's good to understand that FLAGS are just data that you can copy around, save/restore, or even modify if you want)


Your function args and return value are int, which is 32-bit in the System V 32-bit x86 ABI you're using. (links to ABI docs in the tag wiki). Writing a function that only looks at the low 16 bits of its input, and leaves high garbage in the high 16 bits of the output is a bug. The int return value in the prototype tells the compiler that all 32 bits of EAX are part of the return value.

As Michael points out, you seem to be saying that your assignment requires using a 16-bit ADD. That will produce carry, overflow, and other conditions with different inputs than if you looked at the full 32 bits. (BTW, this article explains carry vs. overflow very well.)

Here's what I'd do. Note the 32-bit operand size for the ADD.

global add
section .text

add:
    push  ebp    
    mov   ebp,esp      ; stack frames are optional, you can address things relative to ESP

    mov   eax, [ebp+8]    ; first arg: No need to avoid loading the full 32 bits; the next insn doesn't care about the high garbage.
    add   ax,  [ebp+12]   ; low 16 bits of second arg.  Operand-size implied by AX

    cwde                  ; sign-extend AX into EAX

    mov   ecx, [ebp+16]   ; the pointer arg
    pushf                 ; the simple straightforward way
    pop   edx
    mov   [ecx], dx       ; Store the low 16 of what we popped.  Writing  word [ecx]  is optional, because dx implies 16-bit operand-size
                          ; be careful not to do a 32-bit store here, because that would write outside the caller's object.

    ;  mov esp,ebp   ; redundant: ESP is still pointing at the place we pushed EBP, since the push is balanced by an equal-size pop
    pop ebp
    ret

CWDE (the 16->32 form of the 8086 CBW instruction) is not to be confused with CWD (the AX -> DX:AX 8086 instruction). If you're not using AX, then MOVSX / MOVZX are a good way to do this.


The fun way: instead of using the default operand size and doing 32-bit push and pop, we can do a 16-bit pop directly into the destination memory address. That would leave the stack unbalanced, so we could either uncomment the mov esp, ebp again, or use a 16-bit pushf (with an operand-size prefix, which according to the docs makes it only push the low 16 FLAGS, not the 32-bit EFLAGS.)

; What I'd *really* do: maximum efficiency if I had to use the 32-bit ABI with args on the stack, instead of args in registers
global add
section .text

add:
    mov   eax, [esp+4]    ; first arg, first thing above the return address
    add   ax,  [esp+8]    ; second arg
    cwde                  ; sign-extend AX into EAX

    mov   ecx, [esp+12]   ; the pointer

    pushfw                     ; push the low 16 of FLAGS
    pop     word [ecx]         ; pop into memory pointed to by unsigned short* flags

    ret

Both PUSHFW and POP WORD will assemble with an operand-size prefix. output from objdump -Mintel, which uses slightly different syntax from NASM:

  4000c0:       66 9c                   pushfw 
  4000c2:       66 8f 01                pop    WORD PTR [ecx]

PUSHFW is the same as o16 PUSHF. In NASM, o16 applies the operand-size prefix.


If you only needed the low 8 flags (not including OF), you could use LAHF to load FLAGS into AH and store that.


PUSHFing directly into the destination is not something I'd recommend. Temporarily pointing the stack at some random address is not safe in general. Programs with signal handlers will use the space below the stack asynchronously. This is why you have to reserve stack space before using it with sub esp, 32 or whatever, even if you're not going to make a function call that would overwrite it by pushing more stuff on the stack. The only exception is when you have a .


You C caller:

You're passing a NULL pointer, so of course your asm function segfaults. Pass the address of a local to give the function somewhere to store to.

int add(int a, int b, unsigned short* flags);

int main(void) {
    unsigned short flags;
    int result = add(30000, 36000, &flags);
    printf("%d %#hx\n", result, flags);
    return 0;
}
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • `mov [ecx], edx` can't be right? maybe you meant `mov [ecx], dx` since flags is `unsigned short* flags` (16-bit value). You would have overwritten beyond the 2 bytes allocated for that variable. – Michael Petch Sep 13 '16 at 16:10
  • Okay the `word` directive can be used for clarity but not needed since the size of _DX_ is known to be 16-bits – Michael Petch Sep 13 '16 at 16:16
  • 1
    There is one other issue in your code depending on how you look at this question. If you look at the OPs question they are to do `16-bit add`. Given that FLAGS was suppose to be returned, I inferred that the original assignment (which we don't know about) wanted the FLAGS because of course on a 16-bit add (vs your 32-bit) would set the flags differently So I assumed you pass in 2 32-bit integers, use 16-bit adds, get the flags and then return the value of that 16-bit value (in a 32-bit register _EAX_). – Michael Petch Sep 13 '16 at 16:20
  • Without seeing the assignment hard to tell what representation would have been expected in _EAX_. (Did they have to account for the overflow flag and sign extend it to the 32-bit register? etc) – Michael Petch Sep 13 '16 at 16:25
  • @MichaelPetch: I didn't read all the comments on the other answer before writing mine. Or the question in full detail, either, apparently; you're right, the OP does say to use a 16-bit add in asm. – Peter Cordes Sep 13 '16 at 16:25
  • @MichaelPetch: Given that prototype and the requirement to do a 16-bit add, I think my current answer is sensible. It only takes one more insn (CWDE) to satisfy both the prototype and the 16-bit add requirement. Truncating the inputs to 16 bit before adding, and sign-extending the result, seems good. – Peter Cordes Sep 13 '16 at 16:40
  • It is sensible since you are now accounting for that yes. My comment was in the context of your original 32-bit add. Anything in the spirit of the 16-bit add should suffice (which yours does). – Michael Petch Sep 13 '16 at 16:53
  • 1
    Yup, that's why I said "my current answer". I meant to imply that my previous one didn't. Thanks again for catching my mistakes from skimming too quickly. – Peter Cordes Sep 13 '16 at 16:59
  • @Peter Cordes:Hey thanks for your answer! I have been working hard on that Code the last two days. But somehow I am not able to get the Flags now to the C Program! If I want to print them I always get a Segmentation Fault! Would it be possible that you could show an appropriate c program or the way you would print the flag register? – Helga Schmid Sep 15 '16 at 18:57
  • @HelgaSchmid: updated my answer. You were passing a NULL pointer instead of a pointer to any storage. – Peter Cordes Sep 15 '16 at 19:01
  • g@Peter Cordes: Thx for your fast answer. So the Flags get a decimal number. If i put it into binary than this is the flags right? – Helga Schmid Sep 15 '16 at 19:26
  • @HelgaSchmid: It's a 16-bit integer. It's up to you how you want to print it out. See https://en.wikipedia.org/wiki/FLAGS_register for which bit means what. See also http://stackoverflow.com/a/4839583/224132 for integer -> string of binary digits. – Peter Cordes Sep 15 '16 at 19:29
0

This is just a simple approach. I didn't test it, but you should get the idea...

Just set ESP to the pointer value, increment it by 2 (even for 32-bit arch) and PUSHF like this:

global add
section .text

add:
    push ebp    
    mov ebp,esp
    mov ax,[ebp+8]
    mov bx,[ebp+12]
    add ax,bx
    ; --- here comes the mod
    mov esp, [ebp+16]      ; this will set ESP to the pointers address "unsigned short* flags"
    lea esp, [esp+2]       ; adjust ESP to address above target
    db 66h                 ; operand size prefix for 16-bit PUSHF (alternatively 'db 0x66', depending on your assembler
    pushf                  ; this will save the lower 16-bits of EFLAGS to WORD PTR [EBP+16] = [ESP+2-2]
    ; --- here ends the mod
    mov esp,ebp
    pop ebp
    ret

This should work, because PUSHF decrements ESP by 2 and then saves the value to WORD PTR [ESP]. Therefore it had to be increased before using the pointer address. Setting ESP to the appropriate value enables you to denominate the direct target of PUSHF.

zx485
  • 28,498
  • 28
  • 50
  • 59
  • This seems wrong since the code appears to be 32-bit. flags is `unsigned short* flags` (16-bit value) from the prototype. `pushfd` would write 4 bytes to the flags location (where 2 bytes are allocated) potentially clobbering data in memory next to it. – Michael Petch Sep 13 '16 at 15:00
  • Other issues with the OPs code are that they are using a non-volatile register _EBX_ to do calculations without preserving it. I would probably recommend using a volatile register like _ECX_ rather than _EBX_. – Michael Petch Sep 13 '16 at 15:03
  • @MichaelPetch: Thanks for your comment, I overlooked that the target is `ushort`. Unusual, because the question was about `EFLAGS`. I modified my solution to acknowledge that. – zx485 Sep 13 '16 at 15:07
  • @MichaelPetch: I made sure that the correct operand-size-prefix is used by explicitly incorporating a `66h` byte before `PUSHF`. – zx485 Sep 13 '16 at 15:14
  • 2
    Rather than `db 66h` _NASM_ can override the operand size with `o16` keyword. Might be easier to just use `o16 pushf` . That should produce the prefix you are looking for without the use of `db`. Question says they are using _NASM_ as an assembler. – Michael Petch Sep 13 '16 at 15:15
  • One other bug is that `add esp, 2` will change EFLAGS before the _PUSHF_ . Maybe move the `add ax,bx` after the point where you adjust the stack pointer (but before the _PUSHF_) – Michael Petch Sep 13 '16 at 15:32
  • @MichaelPetch: Thanks again. I fixed that. – zx485 Sep 13 '16 at 15:36
  • Thank you for your answer! I have tried your solution but apperently it doesn´t work. I am trying to modify it right now! The Assignment said that pushfd has to be used so that the entire flag register will be saved on the stack. – Helga Schmid Sep 13 '16 at 15:41
  • @HelgaSchmid : You haven't shown us the actual assignment, we don't know the constraints you are under. `pushf` and `pushfd` actually do the same thing. In 32-bit code they push the entire 32-bit contents of the register. – Michael Petch Sep 13 '16 at 15:42
  • 2
    Modifying ESP isn't safe in general, in a program with signal handlers. I wouldn't recommend it. – Peter Cordes Sep 13 '16 at 15:43
  • @MichaelPetch: Oh interesting point; yeah *if* the caller's pointer pointed to a location within the last 128B of stack, the red-zone would save you. Otherwise signal handlers would just clobber stuff 128B below wherever the caller's pointer pointed. The ESP modification takes more insns than needed anyway, though. – Peter Cordes Sep 13 '16 at 15:47
  • I would have done it this way `mov edx, [ebp+16]` `pushfd` `pop word [edx]` (followed by the function epilogue which would fix the fact the stack is off by 2 because of differing operand sizes on push/pop). Alternatively if you want to avoid the stack mismatch/misalignment `mov edx, [ebp+16]` `pushfd` `pop ecx` `mov [edx], cx` – Michael Petch Sep 13 '16 at 15:58
  • The latter solution in my last comment would have avoided the necessity of having prologue/epilogue code at all. – Michael Petch Sep 13 '16 at 16:08
  • @MichaelPetch: If you're really optimizing, go for PUSHFW to match the 16-bit pop. :) That's what I decided to do for the "fun way" (as opposed to the beginner-friendly way) in my answer. – Peter Cordes Sep 13 '16 at 16:10
  • `pushfw` is the same as `o16 pushf` which I mentioned in an earlier comment as a possible instruction to use here. Many ways to skin this cat. – Michael Petch Sep 13 '16 at 16:18