الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

محاكات (declspec (naked في GCC

بدأه fouad2008 في 16 ديسمبر 2012 · 4 رد · 710 مشاهدة · في لغة C و ++C
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم

حبيت اشاركم معلومة

من المعلوم ان الخاصية

اقتباس
__attribute__ ((naked))
في مترجم GCC مفعلة فقط في معالجات ARM, AVR, MCORE, RX and SPU

بينما في MSVC توجد هذه الخاصية في معالجات i386 , طبعا للذين لا يعرفون الهدف من الخاصية هو انه عندما نضيفها الي الدالة تصبح هذه الاخيرة بدون Prolog/Endlog يعني مثل التسمية : عارية

المهم انه هناك تحايل ان صح التعبير hack لمحاكات هذه الخاصية في معالجات i386 بواسطة المحبوب Gcc

اليكم مثال يوضح الامر

#include <stdio.h>

#ifdef __cplusplus

extern "C"{
		asm("_sqrtd:"
                    "fld qword ptr [esp+4] \n" 
		    "fsqrt   \n"
		    "ret 8 \n"
      );
double  sqrtd(double x);
}
#endif

int main()
{
  double x = 25;
  printf("the sqrt of 25 is : %f \n",sqrtd(x));
  getchar();
}

تم تعديل هذه المشاركة بواسطة fouad2008 في 16 ديسمبر 2012 في 21:01

3
#2

خدعة جيدة و لكن مشكتلها انك لن تستطيع كتابة كود c++/c داخل هذه الـ function و هذا على خلاف ما يحدث مع الإختيار naked.

تم تعديل هذه المشاركة بواسطة C++er في 16 ديسمبر 2012 في 21:47

مدونتي: C++ Tips and Tricks

#3

بارك الله فيك أخي، هذا توضيح بسيط لمن لم يفهم الموضوع.

لو نظرنا للتعليمات الخاصة بالدالة my_print_function في هذا البرنامج:

#include <stdio.h>

int my_print_function(char *str);

int main(int argc, char **argv)
{
	my_print_function("Hello world!");
	return 0;
}

int my_print_function(char *str)
{
	puts(str);
	return 0;
}

سيظهر لنا شيء مثل:

> gdb -q example.exeexample.exe...done.
(gdb) disassemble my_print_function
Dump of assembler code for function my_print_function:
   0x004013ad <+0>: 	push   %ebp 				;
   0x004013ae <+1>: 	mov	%esp,%ebp        	; رأس الدالة
   0x004013b0 <+3>: 	sub	$0x18,%esp   		;
   0x004013b3 <+6>: 	mov	0x8(%ebp),%eax
   0x004013b6 <+9>: 	mov	%eax,(%esp)
   0x004013b9 <+12>:	call   0x401b1c <puts>
   0x004013be <+17>:	mov	$0x0,%eax
   0x004013c3 <+22>:	leave   					; أسفل الدالة
   0x004013c4 <+23>:	ret     					;
   0x004013c5 <+24>:	nop
   0x004013c6 <+25>:	nop
   0x004013c7 <+26>:	nop
End of assembler dump.
(gdb)

الأجزاء التي أشرت إليها (الرأس هو الـprolog وأسفل الدالة هو الـendlog) ليست أجزاء من الدالة بل أجزاء أضافها المصرف ويختلف شكلها مع كل إسلوب إستدعاء. الخاصية naked في بعض المصرفات تطلب من المصرف أن لايضيف تلك الأجزاء، إذا أراد المبرمج إضافتها بنفسه.

البرنامج:

#include <stdio.h>

#ifdef __cplusplus
extern "C" {
#endif
asm("_sqrtd:"
	"fldl 4(%esp);"
	"fsqrt;"
	"ret;"
);
double sqrtd(double x);
#ifdef __cplusplus
};
#endif


int main()
{
	double x = 25;
	printf("the sqrt of 25 is : %f \n", sqrtd(x));
	return 0;
}

طلب من المصرف أن لايعبث بإسم الدالة إن كان المصرف مصرف c++ عن طريق extern "C" [*] وأضاف تعليمة لجلب المتغير الأول الذي مرر للدالة وحسب مربع الرقم. شكل البرنامج الناتج:

   0x0040138c <+0>: 	fldl   0x4(%esp)
   0x00401390 <+4>: 	fsqrt
   0x00401392 <+6>: 	ret

لايمكن لأي دالة يولدها المصرف أن تكون بهذا الصغر.

يلزم وضع extern "C" كما قلت كي لايعدل المصرف إسم الدالة ويعطيك الـlinker خطأ أنه لم يجد الدالة sqrtd. في C++ يمكن كتابة أكثر من دالة لها نفس الإسم:

#include <iostream>

int my_function(int x);
char my_function(char x);
double my_function(double x);
int my_function(int *x);

int main(int argc, char **argv)
{
	int x = 1;

	my_function(1);
	my_function('1');
	my_function(1.0);
	my_function(&x);

	return 0;
}

int my_function(int x)
{
	return x * 2;
}

char my_function(char x)
{
	return x * 2;
}

double my_function(double x)
{
	return x * 2;
}

int my_function(int *x)
{
	return *x * 2;
}

كي يتمكن المصرف من عمل هذا فإنه يعدلّ أسماء تلك الدوال كي يميزرها عن بعض، لايمكن في الحقيقة وضع أكثر من دالة بنفس الإسم. في gcc يسضيف Z ثم طول إسم الدالة 11 ثم إسم الدالة متبوع بنوعها لتصبح أسماء تلك الدوال:

int my_function(int x);   	/* Z11my_functioni  (Z + 11 + my_function + i (= int)) */
char my_function(char x); 	/* Z11my_functionc  (Z + 11 + my_function + c (= char)) */
double my_function(double x);   /* Z11my_functiond  (Z + 11 + my_function + d (= double)) */
int my_function(int *x);  	/* Z11my_functionPi (Z + 11 + my_function + Pi (= pointer to int)) */

ولكل مصرف طريقته، كما قلت هذه ستصبح مشكلة لو لم نستخدم extern "C" لأن المصرف سيبحث عن الدالة sqrtd بينما قام عدلها لـZ5sqrtdd.

3
#4

شكرا اخ C++er على الشرح المتسلسل

هي من بين الاشياء التى لا اتقنها :blush:

#5

جزاك الله خيرا أخي MR.B وشكرا للأخ فؤاد على طرح الموضوع ...

استفدت منه كثيراً ...

هل يوجد توثيق لمصرف الC وال ++C ?

وعندي سؤال آخر من فضلكم ... هل تقصدون بكلمة مصرف Compiler أم شيئا آخر ؟

جزاكم الله خيرا والسلام عليكم

مواضيع مشابهة