Other Basic assembly programming

Emrion

Active Member

Reaction score: 37
Messages: 186

Hello,

I begin to program with NASM under FreeBSD (11.1-RELEASE FreeBSD 11.1-RELEASE #0 r321309).
I use the C lib to get basic functions like console output. To do that, I link with gcc:

nasm -f elf64 prog.asm && gcc -o prog prog.o -lc

My problem is that I can't make mmap() working. Here is the simplest code:
Code:
extern printf
extern exit
extern mmap

%define PROT_NONE   0x00
%define PROT_READ   0x01
%define PROT_WRITE  0x02
%define PROT_EXEC   0x04

%define MAP_SHARED  0x01
%define MAP_PRIVATE 0x02
%define MAP_TYPE    0x0F
%define MAP_FIXED   0x10
%define MAP_ANON    0x20

section .data
    PrintInt db "%lld", 0xA, 0

section .text
global main

main:
    sub rsp,8  ; stack alignement
    mov rdi,0
    mov rsi,4096
    mov rdx,PROT_READ | PROT_WRITE
    mov r10,MAP_ANON  | MAP_PRIVATE
    mov r8,-1
    mov r9,0
               ; rdi, rsi, rdx, r10, r8, r9
    call mmap  ; void* mmap(void* addr, size_t len, int prot, int flags, int fd, off_t offset);
    mov rdi,PrintInt
    mov rsi,rax
    call printf
    
    xor rdi,rdi
    call exit
The answer of mmap() in rax is always -1, which means MAP_FAILED.

I also tried a syscall:
Code:
    mov rax,477 ; AUE_MMAP    STD    { caddr_t mmap(caddr_t addr, size_t len, \
                                       int prot, int flags, int fd, off_t pos); }
    mov rdi,0   ; rdi, rsi, rdx, r10, r8, r9
    mov rsi,4096
    mov rdx,PROT_READ | PROT_WRITE
    mov r10,MAP_ANON  | MAP_PRIVATE
    mov r8,-1
    mov r9,0
    syscall
Here, the return code is 9: Bad file descriptor.

I think it's something obvious but I'm stuck. Can someone help me?
 

leebrown66

Well-Known Member

Reaction score: 129
Messages: 392

Looks like you are trying to program a Linux system. The developers handbook may enlighten you:
Linux is a UNIX® like system. However, its kernel uses the same system-call convention of passing parameters in registers MS-DOS® does. As with the UNIX® convention, the function number is placed in EAX. The parameters, however, are not passed on the stack but in EBX, ECX, EDX, ESI, EDI, EBP:
 
  • Thanks
Reactions: Oko

leebrown66

Well-Known Member

Reaction score: 129
Messages: 392

Your are absolutely correct, it's been a while ;). OK, looking at the ABI specification (here), it looks like:

rdi - 1st arg
rsi - 2nd arg
rdx - 3rd arg
rcx - 4th arg
r8 - 5th arg
r9 - 6th arg

which doesn't gel with the code you are pointing to. Quickly looking through the source, I found /usr/src/amd64/amd64/support.S's copystr function is commented as:
/*
* copystr(from, to, maxlen, int *lencopied) - MP SAFE
* %rdi, %rsi, %rdx, %rcx
*/
which supports using rcx as the 4th arg.

Sorry I can't be more help. I left asm for C when the 486 hit!
 
OP
OP
E

Emrion

Active Member

Reaction score: 37
Messages: 186

As far as I believe my experiments and results, the passing of arguments in registers is well: rdi, rsi, rdx, r10, r8, r9 (which is a bit weird, I admit). rcx isn't implied at all.

I finally found where was the problem, let's say a part of the problem, because I don't understand all the stuff.

There was indeed an obvious error in my code, for I confused #define's from FreeBSD sources headers with Linux ones.
MAP_ANON is equal to 0x1000, not 0x20. The syscall was answering "bad file descriptor" simply because the flag MAP_ANON wasn't set and a fd of -1 isn't a valid file descriptor.

The following code works:
Code:
    mov rax,477 ; AUE_MMAP    STD    { caddr_t mmap(caddr_t addr, size_t len, \
                                  int prot, int flags, int fd, off_t pos); }
    mov rdi,0             ; adress, any
    mov rsi,4096          ; size of region
    mov rdx,3             ; PROT_READ | PROT_WRITE
    mov r10,0x1002        ; MAP_ANON  | MAP_PRIVATE
    mov r8,-1             ; fd, -1 only for MAP_ANON
    mov r9,0              ; offset
    syscall
But... The call to the C function mmap() still doesn't work. I did not find why.
Anyway, I can now continue coding
 

leebrown66

Well-Known Member

Reaction score: 129
Messages: 392

EDIT:
I came across a Solaris ABI that indeed used r10 instead of rcx, but now I can't find it.
Page 148 of the specification states:
1. User-level applications use as integer registers for passing the sequence
%rdi, %rsi, %rdx, %rcx, %r8 and %r9. The kernel interface uses %rdi,
%rsi, %rdx, %r10, %r8 and %r9.

If you can get the C function to work as actual C code, have the compiler leave the intermediary assembler file so you can see how it sets up the call, or gdb it and look from there.
 
OP
OP
E

Emrion

Active Member

Reaction score: 37
Messages: 186

You're right. That was the trick. r10 when invoking syscall and rcx when calling C functions. It's very confusing but I suppose there is a good reason for this subtle and tricky difference. A reason which escapes me.

Thanks to you, I can make my code more homogeneous by exclusively calling C functions instead of mixing them with some kernel syscalls. And more important, to understand my mistake.

I have a question concerning System V. This documentation was right for the passing of arguments but will it be right for all? I need to set an exception handler and I saw some interesting explanations inside. For the few I know System V isn't the same beast than BSD, is it?
 

leebrown66

Well-Known Member

Reaction score: 129
Messages: 392

I have a question concerning System V. This documentation was right for the passing of arguments but will it be right for all? I need to set an exception handler and I saw some interesting explanations inside. For the few I know System V isn't the same beast than BSD, is it?
I think that's just a reflection of the original authors (SCO I believe). You're correct, FreeBSD doesn't even belong to the V5 family, but they adopted what was an industry standard. I can't see they would change it and break compatibility with everything else and I can't find a FreeBSD ABI specific document either.
 

_martin

Aspiring Daemon

Reaction score: 156
Messages: 774

Reading and getting familiar with ABI is important. I think there are better ways of starting to use nasm than to start importing libc functions, but ok :).

You were passing parameters to syscall in regs which is fine. It seems though you've set mmap flags according to Linux - MAP_PRIVATE|MAP_ANONYMOUS = 0x22 there. In FreeBSD it's 0x1002.

Short program to test:

Code:
    xor rdi, rdi
    mov rsi, 0x1000
    mov rdx, 7
    mov r10, 0x1002
    mov r8, -1
    mov r9,0
    mov rax, 0x1dd
    syscall

    xor rax, rax
    inc al
    mov rdi, 0x2a
    syscall
It's been some time I've done debugging in FreeBSD, but hey - finally info proc map works in gdb :).
 
OP
OP
E

Emrion

Active Member

Reaction score: 37
Messages: 186

You were passing parameters to syscall in regs which is fine. It seems though you've set mmap flags according to Linux - MAP_PRIVATE|MAP_ANONYMOUS = 0x22 there. In FreeBSD it's 0x1002.
Thanks, but it's precisely what I discovered by myself. See my post above.

I think there are better ways of starting to use nasm than to start importing libc functions, but ok :).
Some people write C code including asm parts. I just do the opposite. Functions like printf make life easier.

Never used gdb but I should. Tried "info proc map" in gdb, got Undefined info command: "proc map". I guess there is something I didn't understand.
 
OP
OP
E

Emrion

Active Member

Reaction score: 37
Messages: 186

I see you already found my github!
Of course, as well as your related posts here. Time has come to thank you for this excellent work.

By the way, I'm trying to set a signal handler (for SIGSEGV). For the moment, I'm playing with C functions: signal(), sigsetjmp() and siglongjmp(), but I find that too complicated for this simple goal: resume execution at a given address. Do you have any tips?

Finally, I found a simple way ; a hack rather:
Code:
(...)

    mov rdi,SIGSEGV
    mov rsi,exception
    call signal ; signal handler for SIGSEGV set at exception

(...)

exception:
    mov rdi,rsp
    add rdi,0xc8
    mov qword[rdi],ResumeAddress
    ret
I saw that the address of faulty instruction is on the stack. I guess the stack contains the saved context and rip is at rsp+0xC8.
 

_martin

Aspiring Daemon

Reaction score: 156
Messages: 774

Never used gdb but I should. Tried "info proc map" in gdb, got Undefined info command: "proc map". I guess there is something I didn't understand
That would be due to shipped vs ports gdb. I use the latter.
 

topcat

Active Member

Reaction score: 27
Messages: 127

Nice work! Your "hack" is actually doing the right thing. The kernel pushes a struct sigcontext (definition in /usr/src/sys/x86/include/signal.h line 116, for __amd64__ && __BSD_VISIBLE). The sc_rip member is at 0xC8. See also the man page for sigaction(), particularly the EXAMPLES section.

It's legal to modify the struct, and indeed a ptr to non-const is passed to the C handler. So, one can change the ResumeAddress to something else.

Btw if you don't have access to McCusick's book, here is a link to the relevant chapter: Signals

One suggestion I have for full compatibility is that instead of directly offsetting rsp to get to sc_rip as you have done, you could use the passed pointer to the struct, and offset from that. The pointer is in the third argument to the handler, so rdx. I haven't tested this myself, though.
 
OP
OP
E

Emrion

Active Member

Reaction score: 37
Messages: 186

One suggestion I have for full compatibility is that instead of directly offsetting rsp to get to sc_rip as you have done, you could use the passed pointer to the struct, and offset from that. The pointer is in the third argument to the handler, so rdx. I haven't tested this myself, though.
I can tell you that it works. But doing this in the art rules is somewhat long:

Code:
struc sigcontext
   .sc_mask:          RESD 4
   .sc_onstack:       RESQ 1
   .sc_rdi:           RESQ 1
   .sc_rsi:           RESQ 1
   .sc_rdx:           RESQ 1
   .sc_rcx:           RESQ 1
   .sc_r8:            RESQ 1
   .sc_r9:            RESQ 1
   .sc_rax:           RESQ 1
   .sc_rbx:           RESQ 1
   .sc_rbp:           RESQ 1
   .sc_r10:           RESQ 1
   .sc_r11:           RESQ 1
   .sc_r12:           RESQ 1
   .sc_r13:           RESQ 1
   .sc_r14:           RESQ 1
   .sc_r15:           RESQ 1
   .sc_trapno:        RESQ 1
   .sc_fs:            RESW 1
   .sc_gs:            RESW 1
   .sc_flags:         RESQ 1
   .sc_es:            RESW 1
   .sc_ds:            RESW 1
   .sc_err:           RESQ 1
   .sc_rip:           RESQ 1
   .sc_cs:            RESQ 1
   .sc_rflags:        RESQ 1
   .sc_rsp:           RESQ 1
   .sc_ss:            RESQ 1
   .sc_len:           RESQ 1           
   .sc_fpformat:      RESQ 1
   .sc_ownedfp:       RESQ 1
   .sc_fpstate        RESQ 64 ; must be 16 aligned - howto with nasm?
   .sc_fsbase:        RESQ 1
   .sc_gsbase:        RESQ 1
   .sc_xfpustate:     RESQ 1
   .sc_xfpustate_len: RESQ 1
   .sc_spare:         RESQ 4
endstruc


exception:
   mov qword[rdx+sigcontext.sc_rip],ResumeAddress
   ret
Concerning gbd, I installed version 8.0.1 from ports but I get this:
(gdb) info proc map
Not supported on this target.
 

topcat

Active Member

Reaction score: 27
Messages: 127

That's awesome :) Now you have access to the entire context!

What does info proc all say? The target is a core dump? Also using -F dwarf in nasm may help.
 

_martin

Aspiring Daemon

Reaction score: 156
Messages: 774

Emrion: I guess you are aware of it, but if not there's a security feature on by default that disables user to debug program (i.e. doesn't allow to use ptrace syscall) - security.bsd.unprivileged_proc_debug. It has to be set to 1 for you to debug process as user. It's a pitty this is a global setting (no roles/group, etc..).

So you either debug as root or you set it to 1. I'm using gdb 8.1 and info proc map works there.

There's official FreeBSD docs regarding the gdb here.
 
OP
OP
E

Emrion

Active Member

Reaction score: 37
Messages: 186

First of all, I do not know much things about FreeBSD (same apply for the rest ;)).
I wasn't aware of this flag nor where it could be in the system. Anyway, I ever tried in root, just in case, but the result is the same.
Since you mentioned gdb, I use it as a normal user without problem (this is how I found the rip location of sigcontext on the stack).

The installation of gdb via ports brought the executable gdb801 (it's v8.0.1 not 8.1). This is what I do:
nasm -g -f elf64 prog && gcc -o prog prog.o -lc
gdb801 prog
GNU gdb (GDB) 8.0.1 [GDB v8.0.1 for FreeBSD]
*** snip ****
Reading symbols from prog...done.
(gdb) info proc map
Not supported on this target.
(gdb) info proc all
Not supported on this target.
(gdb) info proc
Not supported on this target.


I also tried to compile with -F dwarf but there is no change.
 

_martin

Aspiring Daemon

Reaction score: 156
Messages: 774

Ok, np. So now you know that FreeBSD has this feature. Check the sysctl(8) man page how to use it. To enable it execute as root:
# sysctl security.bsd.unprivileged_proc_debug=1

Now to the gdb -- it has to be version 8.1. Here's the changelog what was changed in gdb. And here's why gdb has that feature. It's a new feature finally supported in FreeBSD's gdb. You need to have fresh ports.

If you don't know how to use info command in gdb, you can invoke help: help info. Info is abbreviation for information command.
Still you can use command maintenance info sections to get the process layout. It's not that fancy but all important information is there.
 

topcat

Active Member

Reaction score: 27
Messages: 127

The advice to refresh/update your system is a good one. I can run the info commands on my fully up-to-date system.
 
OP
OP
E

Emrion

Active Member

Reaction score: 37
Messages: 186

Well, the thing I'm aware is the short life of ports. I once paid my ignorance about this, just after a fresh install of FreeBSD (...).

So, I deinstalled gdb 8.0.1, then I refreshed ports and, at last, installed gdb 8.1. This time info proc works. It gives a lot of details. I must admit that this version of gdb is far most attractive than the one I used.

I also played with sysctl and I saw what I expected: security.bsd.unprivileged_proc_debug is already set to 1 (and I think this is the default since I never touched that).

Thank you to all.
 

debguy

Active Member

Reaction score: 11
Messages: 119

this might be a different angle. compiling asm on linux or freeBSD is almost the same except for sysctl compatibility, unfortunately ;)

mmap is a kernel function C calls. you could compile a C probram that does only that and exist, tell GCC to stop at the ASM (.S) output, aand see "how it's dun"

OR you could contact the freeBSD kernel directly and ask for mmap, instead of asking libc for it

what you do ultimately depends on your objective

my advice is direct hardware access is way way way above your head, mmap is a useless feature (outside of kernel land anyhow), just use fopen() and fread() whether you do it in ASM or C

likely your "playing with asm" and not being paid to write a driver for AMD, so, the simpler the better, KISS. if you were writing a binary blob for AMD you might find the "ASM" wasn't nasm, the nemonics were all differnt from intel nemonics (and so the parsing), and the system didn't run on any 'nix, perhaps.

or, if you got a $5 soc (filled with complicated features an mini power for $5 few or none have yet to make good use of!) it likely comes with a java interface for uploading microcode that only runs under windows 10, again nasm wouldn't stretch there
 
Top