LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 02-27-2023, 12:24 AM   #1
EvanRC
LQ Newbie
 
Registered: Jul 2021
Location: Middle of Northern Colorado, USA
Distribution: Ubuntu
Posts: 9

Rep: Reputation: Disabled
CDECL problem when interfacing NASM and C


Was working on some test code which would allow me to call functions in a C source file from an Assembly source file.


It was getting some really bizarre results, which had nothing to do with the parameters I fed it.


The NASM code in question:
Code:
        SUB RSP, 2 * 8
        MOV [RSP + 8], BYTE 2
        MOV [RSP], BYTE 2
        
        CALL u_CdeclTester
        
        ADD RSP, 16
(The starting portion is equivalent to PUSHing to stack.


The C Code in question:
Code:
uint16_t u_CdeclTester(const uint16_t val1, const uint16_t val2) {
    uint32_t X = u_Testinator();
    X++;
    if ((val1 == 1) && (val2 == 2)) return 0;
    return val1 + val2;
}
When provided the aforementioned inputs, it would return 104, so I decided to decompile the code with objdump. This is the x86-64 disassembly of the entire program, shortened to just the above C code:



Code:
  40113e:    55                       push   rbp
  40113f:    48 89 e5                 mov    rbp,rsp
  401142:    48 83 ec 18              sub    rsp,0x18  {{allocate room for 3 variables?}}
  401146:    89 fa                    mov    edx,edi
  401148:    89 f0                    mov    eax,esi
  40114a:    66 89 55 ec              mov    WORD PTR [rbp-0x14],dx  {{overwrite memory?}}
  40114e:    66 89 45 e8              mov    WORD PTR [rbp-0x18],ax
  401152:    b8 00 00 00 00           mov    eax,0x0
  401157:    e8 cf ff ff ff           call   40112b <putc@plt+0x10b>
  40115c:    89 45 fc                 mov    DWORD PTR [rbp-0x4],eax
  40115f:    83 45 fc 01              add    DWORD PTR [rbp-0x4],0x1
  401163:    66 83 7d ec 01           cmp    WORD PTR [rbp-0x14],0x1
  401168:    75 0e                    jne    401178 <putc@plt+0x158>
  40116a:    66 83 7d e8 02           cmp    WORD PTR [rbp-0x18],0x2
  40116f:    75 07                    jne    401178 <putc@plt+0x158>
  401171:    b8 00 00 00 00           mov    eax,0x0
  401176:    eb 0a                    jmp    401182 <putc@plt+0x162>
  401178:    0f b7 55 ec              movzx  edx,WORD PTR [rbp-0x14]
  40117c:    0f b7 45 e8              movzx  eax,WORD PTR [rbp-0x18]
  401180:    01 d0                    add    eax,edx
  401182:    c9                       leave  
  401183:    c3                       ret
I understand that with CDECL, all parameters are passed through the stack, and that the caller has to manage RAX, RCX and RDX. Given the layout of the C code, I would expect it to at least start like this:
Code:
PUSH RBP
MOV RBP, RSP
MOV DX, [RBP + 0x16]  # load parameter 2
MOV AX, [RBP + 0x08] # load parameter 1

...
But as you can see in the disassembly, it sets DX and AX to the Destination and Source registers respectively, and then goes on to overwrite the memory positions that should have been assigned to the parameters (not only being really weird, but also violating the const attribute applied to the parameters).



I also decompiled it into the nastier AT&T syntax, and it still did the same operation, so it isn't on the fault of the disassembler in objdump. Furthermore, when I set RDI and RSI to zero, the code returned zero.


The compiling code is as follows:
Code:
rm -f "test"
nasm -Wall -t -g -f elf64 -Ox "test.asm" -o "test.o"
gcc -Wall -std=c11 -Wno-attributes -c "utils.c" -o "utils.o"
ld -O9 -s -L/usr/lib/x86_64-linux-gnu --dynamic-linker=/lib64/ld-linux-x86-64.so.2 "utils.o" "test.o" -o "test" -call_shared -lc
rm "test.o" "utils.o"
Compilation finished successfully.
Is there some compiling flag that I missed? Is LD not linking the object code properly? Or is it something else entirely?


I'm completely stumped with this. It's stalled my progress, and no matter what I change, it still uses the RSI/RDI and still screws up what it's supposed to return.
 
Old 02-27-2023, 02:17 AM   #2
NevemTeve
Senior Member
 
Registered: Oct 2011
Location: Budapest
Distribution: Debian/GNU/Linux, AIX
Posts: 4,864
Blog Entries: 1

Rep: Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869Reputation: 1869
What documentation are you using about the parameter passing? I suggest this: https://en.wikipedia.org/wiki/X86_ca...em_V_AMD64_ABI
 
Old 02-27-2023, 07:28 AM   #3
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,781

Rep: Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082Reputation: 2082
You can also ask the compiler to generate code that calls the function:
Code:
#include <stdint.h>
uint16_t u_CdeclTester(const uint16_t val1, const uint16_t val2);

void call_u_CdeclTester()
{
    u_CdeclTester(2, 2);
}
disassembly (compiled at -O1 to simplify a bit):
Code:
call_u_CdeclTester():
        sub     rsp, 8
        mov     esi, 2
        mov     edi, 2
        call    u_CdeclTester(unsigned short, unsigned short)
        add     rsp, 8
        ret
You can see the parameters are not passed on the stack.
 
Old 02-27-2023, 08:30 AM   #4
EvanRC
LQ Newbie
 
Registered: Jul 2021
Location: Middle of Northern Colorado, USA
Distribution: Ubuntu
Posts: 9

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by NevemTeve View Post
What documentation are you using about the parameter passing? I suggest this: https://en.wikipedia.org/wiki/X86_ca...em_V_AMD64_ABI

I see there is that System V convention, but I specifically passed
Code:
__attribute__((__cdecl__))
to the compiler. It also doesn't stand to explain why the disassembled code has calls overwriting part of the stack. System V has no specification for that, but it would appear that the compiler is disregarding the CDECL convention if it is making it in that form.
 
Old 02-27-2023, 11:32 AM   #5
EdGr
Member
 
Registered: Dec 2010
Location: California, USA
Distribution: I run my own OS
Posts: 998

Rep: Reputation: 470Reputation: 470Reputation: 470Reputation: 470Reputation: 470
The disassembled code is completely understandable.

The parameters have to be passed in RDI and RSI because the calling convention says so.

The compiler is copying the parameters to [rbp-0x14] and [rbp-0x18]. These are local variables.

Function u_Testinator has been inlined.

Local variable X is stored at [rbp-0x4]. The compiler is putting local variables in memory because register allocation has not been enabled.
Ed
 
Old 02-27-2023, 01:51 PM   #6
EvanRC
LQ Newbie
 
Registered: Jul 2021
Location: Middle of Northern Colorado, USA
Distribution: Ubuntu
Posts: 9

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by EdGr View Post
The parameters have to be passed in RDI and RSI because the calling convention says so.
As I stated in a previous reply, I'm aware what System V calls for, but I wasn't expecting for GCC to disregard the C convention -- especially with it directly specified -- over System V. Of course, reading into it says that the cdecl convention is only in 32-bit mode unless you write assembly in that form. So, I suppose that's one mystery solved.



Quote:
Originally Posted by EdGr View Post
The compiler is copying the parameters to [rbp-0x14] and [rbp-0x18]. These are local variables.

...

Local variable X is stored at [rbp-0x4]. The compiler is putting local variables in memory because register allocation has not been enabled.

And that does answer the stack overwrite question (I was about to ask if one should subtract from RSP prior to calling, then remembered the decompiled code already has a call to do so).



I had not received instruction regarding System V in my systems architecture class, nor was it mentioned in the assembly programming documentation I had been looking over previously.


However, then, would one place the second parameter into RDI or RSI? The Wikipedia page simply states that the first six integer/pointer variables are passed through RDI, RSI, RDX, RCX, then R8 and R9, so I would assume that the second parameter goes into RSI.
 
Old 02-27-2023, 01:52 PM   #7
EvanRC
LQ Newbie
 
Registered: Jul 2021
Location: Middle of Northern Colorado, USA
Distribution: Ubuntu
Posts: 9

Original Poster
Rep: Reputation: Disabled
Update, confirmed that RSI is the second parameter. Marking as closed.
 
  


Reply

Tags
assembly-language, gcc, linking, nasm, parameters



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
__attribute__((__cdecl__)) or __attribute__((cdecl)) dfatlq Programming 1 01-20-2016 12:02 AM
Why is esi used in 1 nasm script but edi used in another nasm script? abefroman Programming 6 08-26-2015 02:04 AM
Compiling C++ for x86_64 and get warning: 'cdecl' attribute ignored fuzzyBuzz Programming 3 05-27-2012 11:17 AM
calling conventions cdecl ,stdcall 1mela Programming 1 03-28-2010 03:45 PM
Installing cdecl package/tool subramani Linux - Software 2 03-07-2010 07:33 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 05:52 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration