انجمنهای فارسی اوبونتو
کمک و پشتیبانی => برنامهسازی => نویسنده: علی ۵۰۰ در 31 فروردین 1392، 08:09 بظ
-
به نام خداوند بخشنده مهربان
سلام
در کامپایلر gcc و لینوکس با زبان برنامه نویسی C چهطور میشه از اسمبلی استفاده کرد؟ :)
با تشکر
-
اسمبلی بصورت Inline در C نوشته میشه. و با سینتکس AT&T . و در کد C به این شکل نوشته میشه:
#asm
//put your code here
#endasm
میتونید به این شکل اسمبلی بنویسید.
میتونید جداگانه هم نوشته و توسط nasm کامپایلش کنید.
-
حالا این کدی رو که نوشتم درسته؟ اگه درسته باید چهطور کامپایلرش کنم؟ :)
#include <stdio.h>
int main()
{
#asm
mov ah, 9h // function call to print
mov bh, 0 // page number
mov bl, 7 // attribute
mov cx, 5 // number of print
mov al, 03h // heart character
int 10h // interrupt call
#endasm
getchar();
return 0;
}
-
من چون اینطور ننوشتم مطمئن نیستم. فقط این کد رو بزن (با فرض اینکه اسم سورست asm.c باشه!)
gcc asm.c
فکر کنم الان اونی که منفی میده تو کمینه ;D
-
asm.c: In function ‘main’:
asm.c:4:2: error: invalid preprocessing directive #asm
asm.c:5:3: error: unknown type name ‘mov’
asm.c:5:11: error: invalid suffix "h" on integer constant
asm.c:5:11: error: expected identifier or ‘(’ before numeric constant
asm.c:9:11: error: invalid suffix "h" on integer constant
asm.c:10:7: error: invalid suffix "h" on integer constant
asm.c:11:2: error: invalid preprocessing directive #endasm
خب کد مشکل داره. بهتره عبارت Inline assembly in C رو در موتورهای جست و جو سرچ کنید :)
-
البته نوشتن اسمبلی در C یا ++C با کلمه کلیدی asm یا asm_ یا asm__ هست. (اینکه کدومش هست رو شک دارم)
مثلا به صورت زیر:
asm mov $9, %ah
یا
asm {
// multiple assembly codes
}
البته اون سیتکسی که شما نوشتی مال intel هست. سینتکس AT&T یه چیزی شبیه اینیه که من نوشتم. (احتمالا اشتباه نوشتم).
با دستور زیر کامپایل کنید.
gcc yourASM.c
اگه کمی C بلد هستید، میتونید یه برنامه خیلی ساده به زبان C بنویسید و با آپشن S خروجی اسمبلی اون رو ببینید
gcc yourASM.c -S
یه فایل اسمبلی میسازه که تبدیل شده کد شما به زبون اسمبلی با سینتکس AT&T هست. اینطوری راحتتر یاد میگیرید
-
یک نرم افزار ساده که توسط gcc به اسمبلی تبدیل کردم :
.file "hello.c"
.section .rodata
.LC0:
.string "HelloWorld"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2"
.section .note.GNU-stack,"",@progbits
این هم کد C :
#include <stdio.h>
int main()
{
printf("Hello World\n");
return 0;
}
-
در حد اطلاعات خودم این رو بهتون میگم. همه جاشو بلد نیستم.
1. این file. همونیه که توی C یا ++C اینجوری صداش میزنیم __FILE__
2. section یعنی قسمت. فایلهای اجرایی توی ۱۶ و ۳۲ بیتی از این قسمتها استفاده میکنن (در مورد ۶۴ بیت اطلاعی ندارم). هر فایل اجرایی ۴ قسمت داره.
- قسمت متغیرهای مقدار دهی شده.
- قسمت متغیرهای مقدار دهی نشده یا اونایی که اندازشون معلوم نیست و در آینده معلوم میشه. مثل اشاره گرها که قراره بشن آرایه
- قسمت کد برنامه که معمولا با text نشون میدن
- یادم نیست ;D
3. هر تابع موقعی که میخواد کارشو شروع کنه بستگی به calling convention باید یه سری کارا رو بکنه. مثلا توی C calling convention که زبون C ازش استفاده میکنه کار خالی کردن و برگردوندن ثباتهای stack بر عهده تابع هست. اون خطوط pushl %ebp تا پنچ شش خط پایینترش هم برای همینه و همچنین دستور leave. این کارا رو باید تابع انجام بده که میبینید توی main اومده.
4. خط
movl $.LC0, (%esp)
تازه قسمت اصلی اجرا میشه. از label ای که اسمش LC0 هست. آدرس اولین بایتش رو میریزه توی esp. چرا اولین بایت؟ چون چیزی با LC0.$ جمع نشده و اون متن hello world اولین متغیر اون هست.
5. در خط بعد هم تابع puts رو صدا میزنه که توی stdio هست.
6. خط
movl $0, %eax
مقدار eax رو صفر میکنه که متناظر با همون خط return 0 هست.
7. در نهایت هم مراحل خروج از تابع رو میره.
میدونم خیلی ناقصه ولی امیدوارم جاییش رو اشتباه نگفته باشم.
-
+۱ مجید جان :) .
@علی بهمنی جلالی :
اگر حوصله این سبک Assembly نوشتن رو نداری ، میتونی فایلهایی که با سینتکس اینتل نوشتی رو با C لینک کنی.
مثلا این یک سیستم عامل ساده هست که روش لینک کردن رو هم تا حدودی گفته :
http://wiki.osdev.org/C%2B%2B_Bare_Bones
البته این در ++C هست. ولی روش مشابه هست تا حدود زیادی.
اگر هم چیزی میخوای بین C و Assembly زبان --C (سی مینس مینس) رو پیشنهاد میدم :
http://www.cminusminus.org/index.html
-
این یک کد ++C هست که یک ورودی میگیره ، یک واحد بهش اضافه میکنه و در خروجی قرار میده :
#include <iostream>
using namespace std;
int main() {
int i;
cout<<"Enter A Number:";
cin>>i;
int j=++i;
cout<<"Result is :"<<j <<"\n";
return 0;
}
و این نیز کد اسمبلیش :
.file "plusone.cpp"
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.section .rodata
.LC0:
.string "Enter A Number:"
.LC1:
.string "Result is :"
.LC2:
.string "\n"
.text
.globl main
.type main, @function
main:
.LFB969:
.cfi_startproc
.cfi_personality 0,__gxx_personality_v0
.cfi_lsda 0,.LLSDA969
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
movl $.LC0, 4(%esp)
movl $_ZSt4cout, (%esp)
.LEHB0:
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
leal 24(%esp), %eax
movl %eax, 4(%esp)
movl $_ZSt3cin, (%esp)
call _ZNSirsERi
movl 24(%esp), %eax
addl $1, %eax
movl %eax, 24(%esp)
movl 24(%esp), %eax
movl %eax, 28(%esp)
movl $.LC1, 4(%esp)
movl $_ZSt4cout, (%esp)
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
movl 28(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call _ZNSolsEi
movl $.LC2, 4(%esp)
movl %eax, (%esp)
call _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc
.LEHE0:
movl $0, %eax
jmp .L5
.L4:
movl %eax, (%esp)
.LEHB1:
call _Unwind_Resume
.LEHE1:
.L5:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE969:
.globl __gxx_personality_v0
.section .gcc_except_table,"a",@progbits
.LLSDA969:
.byte 0xff
.byte 0xff
.byte 0x1
.uleb128 .LLSDACSE969-.LLSDACSB969
.LLSDACSB969:
.uleb128 .LEHB0-.LFB969
.uleb128 .LEHE0-.LEHB0
.uleb128 .L4-.LFB969
.uleb128 0
.uleb128 .LEHB1-.LFB969
.uleb128 .LEHE1-.LEHB1
.uleb128 0
.uleb128 0
.LLSDACSE969:
.text
.size main, .-main
.type _Z41__static_initialization_and_destruction_0ii, @function
_Z41__static_initialization_and_destruction_0ii:
.LFB973:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
cmpl $1, 8(%ebp)
jne .L6
cmpl $65535, 12(%ebp)
jne .L6
movl $_ZStL8__ioinit, (%esp)
call _ZNSt8ios_base4InitC1Ev
movl $__dso_handle, 8(%esp)
movl $_ZStL8__ioinit, 4(%esp)
movl $_ZNSt8ios_base4InitD1Ev, (%esp)
call __cxa_atexit
.L6:
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE973:
.size _Z41__static_initialization_and_destruction_0ii, .-_Z41__static_initialization_and_destruction_0ii
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB974:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $24, %esp
movl $65535, 4(%esp)
movl $1, (%esp)
call _Z41__static_initialization_and_destruction_0ii
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE974:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .init_array,"aw"
.align 4
.long _GLOBAL__sub_I_main
.hidden __dso_handle
.ident "GCC: (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2"
.section .note.GNU-stack,"",@progbits
برای اسمبل کردنش :
as /path/to/file.s
البته نمیدونم چرا وقتی خروجی اسمبلرش رو اجرا میکنم ارور Permission Denied میده ;D
-
یک کتاب کامل در مورد اسمبلی که در اون کد های اسمبلی به صورت مجزا نوشته می شوند و توسط NASM اسمبل می شوند سپس با کد های زبان C لینک میشوند. به این ترتیب می تونید به صورت دوطرفه به C و اسمبلی دسترسی داشته باشید در حالی که اسمبلی رو با سینتکس اینتل می نویسید:
http://www.drpaulcarter.com/pcasm/ (http://www.drpaulcarter.com/pcasm/)
-
فکر میکنم as فقط اسمبلر هست و برای گرفتن خروجی فایل اجرایی از اون برنامه باید از ld هم استفاده بشه. البته من اطلاعات دقیق ندارم.
-
حوصله ای داریدا ;D
-
یه سؤال میپرسم مسخره نکنین … از سی استفاده کنی کامپایل کنی فرقی داره با این که اول به اسمبلی تبدیلش کنی بعد کامپایل کنی ؟؟؟!!!… البته به جز در مورد سرعت کامپایل بیشتر منظورم بعد از کامپایله …
-
یه سؤال میپرسم مسخره نکنین … از سی استفاده کنی کامپایل کنی فرقی داره با این که اول به اسمبلی تبدیلش کنی بعد کامپایل کنی ؟؟؟!!!… البته به جز در مورد سرعت کامپایل بیشتر منظورم بعد از کامپایله …
راستش رو بخوای به asm تبدیلش کنی ممکنه ارور Permission Denied رو دریافت کنی. مثل من ;D البته نمیدونم دلیلش چیه!
-
یه سؤال میپرسم مسخره نکنین … از سی استفاده کنی کامپایل کنی فرقی داره با این که اول به اسمبلی تبدیلش کنی بعد کامپایل کنی ؟؟؟!!!… البته به جز در مورد سرعت کامپایل بیشتر منظورم بعد از کامپایله …
اگه عملیات بهینه سازی انجام نشه فرقی نمیکنن. چون اون کد C هم همون اسمبلی رو تولید میکنه.
-
راستی اون اسمبلی تولید شده هم با gcc میشه کامپایلاند و اجرا نیز میشه!
{فکر کنم یکی از کاراییهاش اینه که با ++C کد بزنیم و بعد اسمبلیش رو تولید کنیم و بگیم چقدر خفنیم ;D }
-
روش خیلی ساده: http://dorkasaurusrex.blogspot.de/2009/05/debugging-assembly-in-eclipse.html
-
راستی اون اسمبلی تولید شده هم با gcc میشه کامپایلاند و اجرا نیز میشه!
شاید به خاطر نحوه لینک شدن کتابخونه libstdc و فرق اون با ++libstdc باشه. شاید یه مرحله لینک کردن هم این وسط برای ++C هست. من دقیق اطلاع ندارم.