GCC嵌入汇编代码讲解Word格式.docx
《GCC嵌入汇编代码讲解Word格式.docx》由会员分享,可在线阅读,更多相关《GCC嵌入汇编代码讲解Word格式.docx(23页珍藏版)》请在冰点文库上搜索。
![GCC嵌入汇编代码讲解Word格式.docx](https://file1.bingdoc.com/fileroot1/2023-4/29/62846f37-51fd-4f69-ab36-5b579b0f9c03/62846f37-51fd-4f69-ab36-5b579b0f9c031.gif)
of
ecx
to
eax
*/
2.__asm__("
movb
%bh
(%eax)"
/*moves
byte
from
bh
memory
pointed
by
YoumighthavenoticedthathereI’veused
asm
and
__asm__.Botharevalid.Wecanuse__asm__
ifthekeywordasm
conflictswithsomethinginourprogram.Ifwehavemorethanoneinstructions,wewriteoneperlineindoublequotes,andalsosuffixa’\n’and’\t’totheinstruction.Thisisbecausegccsendseachinstructionasastringtoas(GAS)andbyusingthenewline/tabwesendcorrectlyformattedlinestotheassembler.
可能注意到了这里使用了asm和__asm__.都是有效的.如果关键字asm在程序中有冲突,则可以使用__asm__.如果我们需要使用一条以上的汇编指令,我们应该每条占用一行,用双引号括起,并加上'
\n'
和'
\t'
后缀.这是因为gcc把用字符串的格式把汇编指令传给as(GAS),然后利用换行符,把它们转换成正确的汇编格式.
1.__asm__
("
%eax,
%ebx\n\t"
2.
"
$56,
%esi\n\t"
3.
%ecx,
$label(%edx,%ebx,$4)\n\t"
4.
%ah,
(%ebx)"
Ifinourcodewetouch(ie,changethecontents)someregistersandreturnfromasmwithoutfixingthosechanges,somethingbadisgoingtohappen.ThisisbecauseGCChavenoideaaboutthechangesintheregistercontentsandthisleadsustotrouble,especiallywhencompilermakessomeoptimizations.ItwillsupposethatsomeregistercontainsthevalueofsomevariablethatwemighthavechangedwithoutinformingGCC,anditcontinueslikenothinghappened.Whatwecandoiseitherusethoseinstructionshavingnosideeffectsorfixthingswhenwequitorwaitforsomethingtocrash.Thisiswherewewantsomeextendedfunctionality.Extendedasmprovidesuswiththatfunctionality.
如果我们的代码里使用了寄存器,并且在返回的时候没有还原它,这将有坏的情况发生.因为GCC并不知道寄存器的值改变了,特别是编译器对代码进行优化的时候.编译器会认为,那些存放变量的寄存器,我们并没有改变它,然后继续自己的优化.为了避免这种情况,要么,我们不改变寄存器的值,要么,汇编函数返回之前,还原寄存器使用前的值,或者等着代码崩溃(waitforsomethingtocrash).正是由于存在这样的问题,我们需要使用"
ExtendedAsm"
.它将提供给我们扩展功能,解决上边的问题.
5.
ExtendedAsm.
Inbasicinlineassembly,wehadonlyinstructions.Inextendedassembly,wecanalsospecifytheoperands.Itallowsustospecifytheinputregisters,outputregistersandalistofclobberedregisters.Itisnotmandatorytospecifytheregisterstouse,wecanleavethatheadachetoGCCandthatprobablyfitintoGCC’soptimizationschemebetter.Anywaythebasicformatis:
在基本嵌入汇编格式中,我们只使用了指令.在扩展汇编中,我们还可以指定更多操作.它允许我们指定输入寄存器,输出寄存器和变化表(clobberlist).
我们并不一定要指定使用哪些寄存器.我们可以把这件事情交给GCC去做.
扩展汇编的格式如下:
1.asm
(
assembler
template
:
output
operands
optional
input
list
clobbered
registers
5.
Theassemblertemplateconsistsofassemblyinstructions.Eachoperandisdescribedbyanoperand-constraintstringfollowedbytheCexpressioninparentheses.Acolonseparatestheassemblertemplatefromthefirstoutputoperandandanotherseparatesthelastoutputoperandfromthefirstinput,ifany.Commasseparatetheoperandswithineachgroup.Thetotalnumberofoperandsislimitedtotenortothemaximumnumberofoperandsinanyinstructionpatterninthemachinedescription,whicheverisgreater.
这个模板由若干条汇编指令组成,每个操作数(括号里C语言的变量)都有一个限制符(“”中的内容)加以描述.冒号用来分割输入的操作和输出的操作.如果每组内有多个操作数,用逗号分割它们.
操作数最多为10个,或者依照具体机器而异.
Iftherearenooutputoperandsbutthereareinputoperands,youmustplacetwoconsecutivecolonssurroundingtheplacewheretheoutputoperandswouldgo.
如果没有输出操作,但是又有输入,你必须使用连续两个冒号,两个连续冒号中无内容,表示没有输出结果的数据操作.
Example:
cld\n\t"
rep\n\t"
stosl"
no
c"
(count),
a"
(fill_value),
D"
(dest)
6.
%ecx"
%edi"
7.
Now,whatdoesthiscodedo?
Theaboveinlinefillsthe
fill_value
count
timestothelocationpointedtobytheregisteredi.Italsosaystogccthat,thecontentsofregisterseax
andedi
arenolongervalid.Letusseeonemoreexampletomakethingsmoreclearer.
上面这段代码做了什么?
这段内嵌汇编把fill_value,count装入寄存器,同时告知GCC,clobberlist目录中的寄存器eax,edi,已经改变.
我们来看下一个例子:
1.int
a=10,
b;
2.asm
%1,
%%eax;
%%eax,
%0;
=r"
(b)
r"
(a)
register
Herewhatwedidiswemadethevalueof’b’equaltothatof’a’usingassemblyinstructions.Somepointsofinterestare:
代码目的是让'
b'
的值与'
a'
的值相等.
∙"
b"
istheoutputoperand,referredtoby%0and"
istheinputoperand,referredtoby%1.
isaconstraintontheoperands.We’llseeconstraintsindetaillater.Forthetimebeing,"
saystoGCCtouseanyregisterforstoringtheoperands.outputoperandconstraintshouldhaveaconstraintmodifier"
="
.Andthismodifiersaysthatitistheoutputoperandandiswrite-only.
∙Therearetwo%’sprefixedtotheregistername.ThishelpsGCCtodistinguishbetweentheoperandsandregisters.operandshaveasingle%asprefix.
∙Theclobberedregister%eaxafterthethirdcolontellsGCCthatthevalueof%eaxistobemodifiedinside"
asm"
soGCCwon’tusethisregistertostoreanyothervalue.
∙'
是要输出的数据,%0也指它。
'
是输入的数据,%1也指它。
r'
是对操作数的约束。
呆会在详细了解。
暂时这样理解,‘r’告诉GCC选择一个可用的寄存器来保存这个操作数。
输出操作数,应该使用‘=’,表示这个数据只写。
∙双%%前缀,指明这是一个寄存器名。
单%指明操作数。
这帮组GCC辨别操作数和寄存器。
∙第三个冒号后边,这个变化表(clobberlist)里的寄存器%eax,告诉gcc声明的寄存器值已经改变,这样,GCC不会在其他地方使用这个寄存器了。
Whentheexecutionof"
iscomplete,"
willreflecttheupdatedvalue,asitisspecifiedasanoutputoperand.Inotherwords,thechangemadeto"
inside"
issupposedtobereflectedoutsidethe"
.
当这段汇编代码执行完毕,'
变量将会存储这个结果,,正如例子里声明这个变量为输出。
换句话说,'
用来反映汇编程序里值的变化。
Nowwemaylookeachfieldindetail.
现在,深入的理解每一块,看看细节。
5.1AssemblerTemplate.
TheassemblertemplatecontainsthesetofassemblyinstructionsthatgetsinsertedinsidetheCprogram.Theformatislike:
eithereachinstructionshouldbeenclosedwithindoublequotes,ortheentiregroupofinstructionsshouldbewithindoublequotes.Eachinstructionshouldalsoendwithadelimiter.Thevaliddelimitersarenewline(\n)andsemicolon(;
).’\n’maybefollowedbyatab(\t).Weknowthereasonofnewline/tab,right?
.OperandscorrespondingtotheCexpressionsarerepresentedby%0,%1...etc.
这个汇编模板包含一套完整的汇编指令,帮助在c语言内嵌入汇编语言。
具体格式如下:
每条指令应该加上双括号,或者给整套汇编指令加上双括号(如,最后一个例子)。
每条指令结尾都应加上结束符,合法的结束符有(\n)和(;
),或许还应该在\n后边加上一个\t,我们应该了解原因吧?
括号里的若干操作数,依次对应%0,%1。
。
等。
5.2Operands.
Cexpressionsserveasoperandsfortheassemblyinstructionsinside"
.Eachoperandiswrittenasfirstanoperandconstraintindoublequotes.Foroutputoperands,there’llbeaconstraintmodifieralsowithinthequotesandthenfollowstheCexpressionwhichstandsfortheoperand.ie,
constraint"
(Cexpression)isthegeneralform.Foroutputoperandsanadditionalmodifierwillbethere.Constraintsareprimarilyusedtodecidetheaddressingmodesforoperands.Theyarealsousedinspecifyingtheregisterstobeused.
内嵌的汇编指令需要C变量为其提供一个操作数,这个操作数应加上括号。
以输出操作为例,首先会有一个限制符,然后跟上C变量,运算结果将存入这个变量。
双引号内的“限制符”是一个规定的格式。
在输出操作中,这个限制符会额外多一个符号(=)。
限制符主要用来决定操作数的寻址方式。
同时还可指定使用某一个寄存器。
Ifweusemorethanoneoperand,theyareseparatedbycomma.
Intheassemblertemplate,eachoperandisreferencedbynumbers.Numberingisdoneasfollows.Ifthereareatotalofnoperands(bothinputandoutputinclusive),thenthefirstoutputoperandisnumbered0,continuinginincreasingorder,andthelastinputoperandisnumberedn-1.Themaximumnumberofoperandsisaswesawintheprevioussection.
Outputoperandexpressionsmustbelvalues.Theinputoperandsarenotrestrictedlikethis.Theymaybeexpressions.Theextendedasmfeatureismostoftenusedformachineinstructionsthecompileritselfdoesnotknowasexisting;
-).Iftheoutputexpressioncannotbedirectlyaddressed(forexample,itisabit-field),ourconstraintmustallowaregister.Inthatcase,GCCwillusetheregisterastheoutputoftheasm,andthenstorethatregistercontentsintotheoutput.
Asstatedabove,ordinaryoutputoperandsmustbewrite-only;
GCCwillassumethatthevaluesintheseoperandsbeforetheinstructionaredeadandneednotbegenerated.Extendedasmalsosupportsinput-outputorread-writeoperands.
如果我们不止一个操作(有输入,有输出),就必须使用冒号将他们分开。
在标准汇编模板中,每个操作数会有一个Number与之对应。
如果我们一共使用了n个操作数,那么输出操作里的第一个操作数就排0号,之后递增,所以最后一个输出操作的操作数编号为n-1。
操作数的最多个数,前边已经提到过了。
(一般最多10个或者某些机器指令支持更多)
输出操作的表达式必须是数值,输入操作没有这个限制,他可能是表达式。
扩展汇编常常用于实现机器平台自身特殊的指令,编译器可能并不能识别他们:
-)。
如果输出表达式不能直接被寻址(比如,他是一个位字段),我们就应该使用“限制符”指定一个寄存器。
这样,GCC会使用此寄存器存储输出结果,然后再将寄存器的值存入输出操作数。
Sonowweconcentrateonsomeexamples.Wewanttomultiplyanumberby5.Forthatweusetheinstructionlea.
我们现在分析几个例子。
我们想给一个数乘以5。
因此,我们使用lea指令:
(汇编语句leal(r1,r2,4),r3语句表示r1+r2*4→r3。
这个例子可以非常快地将x乘5。
)
leal
(%1,%1,4),
%0"
(five_times_x)
(x)
Hereourinputisin’x’.Wedidn’tspecifytheregistertobeused.GCCwillchoosesomeregisterforinput,oneforoutputanddoeswhatwedesired.Ifwewanttheinputandoutputtoresideinthesameregister,wecaninstructGCCtodoso.Hereweusethosetypesofread-writeoperands.Byspecifyingproperconstraints,herewedoit.
这里,输入一个变量x,我们并没指定特定的寄存器来存储它,GCC会选择一个(“r”表示gcc选择)。
如我们所要求的,gcc会自动选择两个寄存器,一个给input,一个给output。
如果我们想给input和output指定同一个寄存器,我们可以要求GCC这样做(通过更改“限制符”内容)。
(%0,%0,4),
0"
Nowtheinputandoutputoperandsareinthesameregister.Butwedon’tknowwhichregister.Nowifwewanttospecifythatalso,thereisaway.
上例,我们就让input和output使用同一个寄存器,但是不知道具体哪一个。
(如果输入操作的限制符为0或为空,则说明使用与相应输出一样的