LINUX.ORG.RU

NASM, GDB, LD


2

2

Как-то решил я поробывать перейти с MASM на YASM. Наверное отому, что уже долго использую линукс.

Так вот, у меня возникли вопросы, на которые, я думаю, мне помогут найти ответы здесь.

yasm -f elf64 -g stabs pr.asm -o pr.o
gcc pr.o -o pr

Транслируется и линкуется нормально, но: 1. Возникает ошибка сегментации при вызывое функции puts 2. gdb не правильно отображает точки остановок в псевдоокошке свверху, где отображается код, если запустить gdb c параметром -tui

Вот код программы (она пока ничего не делает):


BITS 64
CPU Bulldozer


extern puts
global main

;;--------------------------------------------------------
;секция инициализированных переменных
SECTION .data           
    promt1: db "Starting", 0Ah, 0h     
    promt2: db "Result:", 0h
    promt3: db "Allocation error!", 0Ah, 0h
    str_funloaded: db "Loaded overlay function: ", 0h
    str_exit: db "Bye!", 0Ah, 0h
    str_calling: db "Calling overlay function...", 0Ah,  0h
    nline: db 0Ah, 0h
    o_sum: db "o_sum.bin", 0 ;путь к программе
    o_div: db "o_div.bin", 0 
    o_mul: db "o_mul.bin", 0
    o_sub: db "o_sub.bin", 0
    src_str: db "10 + 20", 0
;----------------------------------------------------------
;секция сегмент подгрузки оверлейных функций
SECTION .bss
 ostart:	resb 10
;----------------------------------------------------------
	;исполняемы код программы	 
SECTION .text
; **********************************************
;подсчет длины строки
; вход: rdx смещение строки в ds
; выход: rcx длина строки
astrlen:
    xor rcx, rcx
    push rbx
    
    mov rbx, rdx ;помещаем адрес строки в ebx
    
    strlen_start:    
    cmp byte [rbx], 0 ;сравниваем текущий символ строки с 0 символом
    je strlen_exit
    inc rbx		;увеличиваем счетчик
    inc rcx
    jmp strlen_start
    strlen_exit:
    pop rbx
    ret
; ************************************************ 
; печать строки
print:	
	push rax
	push rbx
	push rcx
	push rdx
	;подготовка к системному вызову write
	;ssize_t write(int fildes, const void *buf, size_t nbyte);
         mov rax, 4
	 ;помещаем в ebx stdout fd
	 mov rbx, 1
	 mov r8, rdx
	 call astrlen
	 mov rdx, rcx
	 ;в ecx помещаем адрес строки для печати	 
	 mov rcx, r8
	 ;вызов системного прерывания linux
	 int 80h
	 pop rdx
	 pop rcx
	 pop rbx
	 pop rax
	 ret
; ***************************************************
; загрузка оверлейной функции
run:
	mov r8, rbx
	;int open(const char *path, int oflag, ...  );
	mov rax, 5	;системмный вызов open 
	mov rcx, 0000000b
	int 80h
	;ssize_t read(int fildes, void *buf, size_t nbyte);
	mov rbx, rax
	mov rax, 3	;системный вызов read
	mov rcx, ostart
	mov rdx, 10	;размер буфера для записи оверлейной функции
	;закрыываем файл
	int 80h
	mov rax, 6
	int 80h
	;вывод сооxбщения об успешном завершении операции
	mov rdx, str_funloaded
	call print
	
	mov rdx, r8
	call print
	
	mov rdx, nline
	call print
	ret
; ***************************************************
main:
    mov rdx, promt1
    call print
    
    push  promt2
    call puts
    ; обработка параметров командной строки
    ;mob rbx, o_sum
    ;call run
    ;выход из программы 
    exit:
	mov rdx, str_exit
	call print
	mov rax, 1
	mov rbx, 0
	int 80h
    error:
	;print promt3

Ошибка сегментации, когда функция put вызывает функцию strrlen: Вот backtrace:

                            Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7abc3a1 in __strlen_sse2 () from /lib/libc.so.6
(gdb) bt
#0  0x00007ffff7abc3a1 in __strlen_sse2 () from /lib/libc.so.6
#1  0x00007ffff7aa56ec in puts () from /lib/libc.so.6
#2  0x000000000040058a in main () at lab4_x64.asm:112

Еще такой вопрос к знатокам дела: В 64 битных процессорах отказались от сегментных регистров? Или как? Потому что, yasm при трансляции сообщил, что сегментные регистры игнорируются. Я понимаю, что в 32 битных их оставили для совместимости. Или я не так понял что-то?



Последнее исправление: denisnet (всего исправлений: 2)

    push  promt2
    call puts

Ты, видимо, хотел сказать «mov rdi, prompt2», ибо http://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions

Ошибка сегментации происходит при попытке чтения памяти недоступной для чтения, потому что в качестве первого аргумента передан ни разу не указатель на строку.

kim-roader ★★
()

Системный вызов нужно делать так:

        mov rax, SYS_write ;; SYS_write from <sys/syscall.h>
        mov rdi, STDOUT ;; = 1
        mov rsi, string
        mov rdx, string_len
        syscall

http://www.x86-64.org/documentation/abi.pdf :

The Linux AMD64 kernel uses internally the same calling conventions as user- level applications (see section 3.2.3 for details). User-level applications that like to call system calls should use the functions from the C library. The interface between the C library and the Linux kernel is the same as for the user-level appli- cations with the following differences:

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.

2. A system-call is done via the syscall instruction. The kernel destroys registers %rcx and %r11.

3. The number of the syscall has to be passed in register %rax.

4. System-calls are limited to six arguments, no argument is passed directly on the stack.

5. Returning from the syscall, register %rax contains the result of the system-call. A value in the range between -4095 and -1 indicates an error, it is -errno.

6. Only values of class INTEGER or class MEMORY are passed to the kernel.

quasimoto ★★★★
()
Ответ на: комментарий от kim-roader

Thanks. Я просто и подумать не мог, что в x64_86 своя декларация передачи параметров отличная от cdecl.

denisnet
() автор топика
Ответ на: комментарий от quasimoto

syscall - так такую команду не поддерживает yasm. Ведь системный вызов происходит в 80h прерывании. Я не проверял на трансляторе, но в документации к нему не видел такого.

denisnet
() автор топика
Ответ на: комментарий от denisnet

For system calls, R10 is used instead of RCX.

Странно, а почему тогда у меня срабатывает мой 4 системный вызов?

denisnet
() автор топика
Ответ на: комментарий от quasimoto

Да, согласен. У меня в исходнике с помощью директивы CPU был выставоен другой процессор.

Достаточно поставить 686-ой. Насколько я понял, эта инструкция - аалог вызова прерывания 80h для линукс, и наверное 21h для windows. Но быстрее, и поддерживатеся не всеми процессорами.

denisnet
() автор топика
Ответ на: комментарий от denisnet

Может кто подскажет, что надо прописать в прототипе своей функции на C, чтобы моя либа .so смогла слинковаться с программой на C? Не нашел в документации к YASM. Или не там ищу.

gcc main.o -L. -lc -o main 
main.o: In function `no symbol':
main.asm:(.text+0xbf): undefined reference to `calc'
collect2: ошибка: выполнение ld завершилось с кодом возврата 1
make: *** [build] Ошибка 1

В либе c.so во такая функция.

int calc(char *src_string)

denisnet
() автор топика
Ответ на: комментарий от denisnet

В соглашении x86_64 AMD V ABI говорится:

For system calls, R10 is used instead of RCX.

Но я не использую R10 вместо RCX. Т.е. яиспользую RCX для передачи параметров, и все работает. А если использую R10, то не работает. Или здесь описан случай для ситемного вызова вызываемого с помощью инструкии syscall?

denisnet
() автор топика
Ответ на: комментарий от denisnet

под системным вызовом в amd64 подразумевается syscall и ни что иное
читай доки не по диагонали

anonymous
()
Ответ на: комментарий от anonymous

тогда что вызывается у меня в прерывании 80h

print:	
	push rax
	push rbx
	push rcx
	push rdx
	;подготовка к системному вызову write
	;ssize_t write(int fildes, const void *buf, size_t nbyte);
         mov rax, 4
	 ;помещаем в ebx stdout fd
	 mov rbx, 1
	 mov r8, rdx
	 call astrlen
	 mov rdx, rcx
	 ;в ecx помещаем адрес строки для печати	 
	 mov rcx, r8
	 ;вызов системного прерывания linux
	 int 80h
	 pop rdx
	 pop rcx
	 pop rbx
	 pop rax
	 ret
denisnet
() автор топика
Ответ на: комментарий от anonymous

Я понимаю, что системный вызов. Только, что вы подразумевали в прошлом посте про syscall.Т.е. я могу же использовать как 80h прерывание, для систтемных вызовов, так и syscall. Или я не так понял?

denisnet
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.