Pitanje:
Prepoznavanje funkcije promjenjivih arg
Mellowcandle
2013-03-21 01:34:26 UTC
view on stackexchange narkive permalink

Kako bi mogla izgledati funkcija argumenta C kao što je printf (format char *, ...) kada se rastavlja?

Je li uvijek prepoznata pozivanjem konvencije ili postoje li još načini da se to identificira?

Dva odgovori:
#1
+18
Igor Skochinsky
2013-03-21 05:14:36 UTC
view on stackexchange narkive permalink

U nekim je arhitekturama vrlo jednostavno, a u drugima nije baš očito. Opisaću nekoliko poznatih.

SystemV x86_64 (Linux, OS X, BSD)

Vjerojatno najlakše prepoznati. Zbog odlučne odluke o određivanju broja korištenih XMM registara u al , većina funkcija vararg započinje ovako:

  push rbp mov rbp, rsp sub rsp, 0E0h mov [rbp + var_A8], rsi mov [rbp + var_A0], rdx mov [rbp + var_98], rcx mov [rbp + var_90], r8 mov [rbp + var_88], r9 movzx eax, al lea rdx, ds: 0 [rax * 4] lea rax, loc_402DA1 sub rax, rdx lea rdx, [rbp + var_1] jmp rax movaps xmmword ptr [rdx-0Fh], xmm7 movaps xmmword ptr [rdx-1Fh], xmm6 movaps xmmword ptr [rdx-2Fh ], xmm5 movaps xmmword ptr [rdx-3Fh], xmm4 movaps xmmword ptr [rdx-4Fh], xmm3 movaps xmmword ptr [rdx-5Fh], xmm2 movaps xmmword ptr [rdx-6Fh], xmm1 movaps xmmx ptr xmmword pmm ], xmm0loc_402DA1:  

Obratite pažnju na to kako koristi al da bi utvrdio koliko xmm registara će se preliti na stog.

Windows x64 aka AMD64

U Win64-u je to manje očito, ali evo o ne znak: registri koji odgovaraju eliptičnim parametrima uvijek se izlijevaju na stog i na položaje koji se poklapaju s ostatkom argumenata prosljeđenih na stogu. Npr. evo prologa printf :

  mov rax, rsp mov [rax + 8], rcx mov [rax + 10h], rdx mov [rax + 18h] , r8 mov [rax + 20h], r9  

Ovdje rcx sadrži fiksni argument format , a eliptični argumenti se prosljeđuju u rdx , r8 i r9 , a zatim na hrpi. Možemo primijetiti da su rdx , r8 i r9 pohranjeni točno jedan za drugim i odmah ispod ostalih argumenata koji počinju na rsp + 0x28 . Područje [rsp + 8..rsp + 0x28] rezervirano je upravo u tu svrhu, ali funkcije ne-vararg često tamo ne pohranjuju sve argumente registra ili ponovno koriste to područje za lokalne varijable. Na primjer, evo prologa funkcije non -vararg:

  mov [rsp + 10h], rbx mov [rsp + 18h], rbp mov [rsp + 20h] , rsi  

Možete vidjeti da koristi rezervirano područje za spremanje nepostojanih registara, a ne za izlijevanje argumenata registra.

ARM

Konvencija o ARM pozivima koristi R0 - R3 za prve argumente, pa ih funkcije vararga moraju preliti na stek kako bi se poravnale s ostatkom parametara proslijeđenih u stog. Tako ćete vidjeti R0 - R3 (ili R1 - R3 ili R2 - R3 ili samo R3 ) koji se guraju u stog, što obično se ne događa u ne-vararg funkcijama. Nije 100% pouzdan pokazatelj - na pr. Microsoftov kompajler ponekad gura R0 - R1 na stog i pristupa im pomoću SP , umjesto da se premješta u druge registre i koristi to. Ali mislim da je to prilično pouzdan znak za GCC. Evo primjera GCC-kompilirane funkcije:

  STMFD SP!, {R0-R3} LDR R3, = dword_86090STR LR, [SP, # 0x10 + var_14]! LDR R1, [SP, # 0x14 + varg_r0]; formatLDR R0, [R3]; sADD R2, SP, # 0x14 + varg_r1; argBL vsprintfLDR R3, = dword_86094MOV R2, # 1STR R2, [R3] LDR LR, [SP + 0x14 + var_14], # 4ADD SP, SP, # 0x10RET  

To je očito vararg funkcija jer poziva vsprintf , a možemo vidjeti da je R0 - R3 gurnut odmah na početku (možete ne pritiskajte ništa drugo prije toga, jer su potencijalni argumenti steka prisutni na SP , pa im zato moraju prethoditi R0 - R3 ).

Sjajno, hvala što ste primjerima razbili različite scenarije!
#2
+10
Rolf Rolles
2013-03-21 01:40:29 UTC
view on stackexchange narkive permalink

(Moj odgovor je specifičan za x86).

Interno u odnosu na funkciju, izgleda kao i svaka druga funkcija. Jedina razlika je u tome što će u nekom trenutku tijekom funkcije zauzeti (stack) adresu posljednjeg ne-varijabilnog argumenta i povećati je za veličinu riječi na platformi; to se zatim koristi kao pokazivač na bazu argumenata varijable. Izvana za funkciju primijetit ćete da se različiti brojevi argumenata prosljeđuju kao parametri funkciji (a obično će jedan od ne-varijabilnih argumenata biti neki očiti pokazatelj kao funkcija varijabilnog argumenta, poput tvrdo kodiranog niza formata ili nešto slično). Funkcije varijabilnih argumenata ne mogu biti __stdcall , jer se __stdcall oslanja na unaprijed sastavljene upute ret XXh , dok je poanta funkcije varijabilnog argumenta da je nepoznata količina parametara može se proslijediti. Stoga ove funkcije moraju biti __cdecl , tj. Pozivatelj mora ispraviti stog kako bi uklonio sve potisnute argumente.



Ova pitanja su automatski prevedena s engleskog jezika.Izvorni sadržaj dostupan je na stackexchange-u, što zahvaljujemo na cc by-sa 3.0 licenci pod kojom se distribuira.
Loading...