Which Order Are Registers Used To Store Arguments
In this tutorial, you'll look at registers the CPU uses and explore and alter parameters passed into part calls. You'll also learn virtually mutual Apple tree calculator architectures and how their registers are used within a function. This is known as an compages's calling convention.
Knowing how assembly works and how a specific compages's calling convention works is an extremely important skill to accept. It lets you observe function parameters y'all don't have the source code for and lets y'all change the parameters passed into a function. In addition, it's sometimes even meliorate to go to the associates level because your source code could take different or unknown names for variables yous're not aware of.
For example, let's say you ever wanted to know the second parameter of a function call, regardless of what the parameter's name is. Knowledge of associates gives yous a great base layer to manipulate and find parameters in functions.
Assembly 101
Expect, then what's assembly again?
Accept you ever stopped in a function y'all didn't have source lawmaking for, and saw an onslaught of retentivity addresses followed by scary, short commands? Did you lot huddle in a brawl and quietly whisper to yourself yous'll never look at this dumbo stuff once more? Well… that stuff is known as assembly!
Hither's a picture show of a backtrace in Xcode, which showcases the assembly of a function within the Simulator.
Looking at the epitome above, the assembly can be cleaved into several parts. Each line in a assembly didactics contains an opcode, which can be idea of every bit an extremely simple instruction for the computer.
Then what does an opcode look like? An opcode is an instruction that performs a simple task on the computer. For example, consider the post-obit snippet of associates:
pushq %rbx subq $0x228, %rsp movq %rdi, %rbx
In this block of assembly, you see three opcodes, pushq
, subq
, and movq
. Think of the opcode items as the action to perform. The things following the opcode are the source and destination labels. That is, these are the items the opcode acts upon.
In the above case, at that place are several registers, shown every bit rbx
, rsp
, rdi
, and rbp
. The %
earlier each tells you this is a register.
In addition, you can too find a numeric constant in hexadecimal shown as 0x228
. The $
before this abiding tells you information technology's an accented number.
There'south no need to know what this lawmaking is doing at the moment, since yous'll commencement need to learn about the registers and calling convention of functions.
Notation: In the to a higher place example, take note in that location are a bunch of %
's and $
's that precede the registers and constants. This is how the disassembler formats the assembly. However, there are two main ways that assembly tin can be showcased. The first is Intel assembly, and the 2nd is AT & T assembly.
By default, Apple's disassembler tools ship with assembly displayed in the AT&T format, every bit it is in the example in a higher place. Although this is a good format to work with, information technology can admittedly be a footling hard on the caput.
x86_64 vs ARM64
As a developer for Apple platforms, there are ii main architectures you lot'll deal with when learning assembly: x86 _ 64 architecture and ARM64 compages. x86_64 is the compages most likely used on your macOS figurer, unless you are running an "aboriginal" Macintosh.
x86_64 is a 64-fleck architecture, which means every address tin hold up to 64 1s or 0s. Alternatively, older Macs employ a 32-bit compages, simply Apple stopped making 32-bit Macs at the terminate of the 2010's. Programs running under macOS are likely to be 64-fleck compatible, including programs on the Simulator. That being said, even if your macOS is x86_64, information technology tin can nevertheless run 32-bit programs.
If you have whatsoever doubt of what hardware compages you're working with, yous tin go your computer's hardware architecture past running the following command in Terminal:
uname -m
ARM64 compages is used on mobile devices such as your iPhone where limiting energy consumption is critical.
ARM emphasizes ability conservation, so it has a reduced set of opcodes that help facilitate free energy consumption over complex assembly instructions. This is good news for you, because there are fewer instructions for you to learn on the ARM architecture.
Here'south a screenshot of the aforementioned method shown earlier, except this fourth dimension in ARM64 assembly on an iPhone seven:
in many of their devices, but have since moved to 64-flake ARM processors. 32-chip devices are almost obsolete as Apple has phased them out through various iOS versions. For example, the iPhone 4s is a 32-bit device which is not supported in iOS 10. All that remains in the 32-bit iPhone lineup is the iPhone 5, which iOS ten does back up.
Interestingly, all Apple Watch devices are currently 32-scrap. This is probable considering 32-chip ARM CPUs typically draw less ability than their 64-bit cousins. This is really important for the watch as the battery is tiny.
Since it's best to focus on what you'll need for the hereafter, Advanced Apple Debugging & Opposite Engineering will focus primarily on 64-scrap assembly for both architectures. In addition, you'll start learning x86_64 assembly first and and so transition to learning ARM64 assembly so you don't go confused. Well, not too confused.
x86_64 Register Calling Convention
Your CPU uses a set of registers in order to dispense data in your running program. These are storage holders, just like the RAM in your computer. However they're located on the CPU itself very close to the parts of the CPU that need them. So these parts of the CPU tin access these registers incredibly quickly.
Most instructions involve one or more registers and perform operations such as writing the contents of a register to memory, reading the contents of memory to a register or performing arithmetic operations (add together, subtract, etc.) on two registers.
In x64 (from hither on out, x64 is an abbreviation for x86_64), there are xvi general purpose registers used by the machine to manipulate data.
These registers are RAX, RBX, RCX, RDX, RDI, RSI, RSP, RBP and R8 through R15. These names will non hateful much to y'all now, but y'all'll explore the importance of each register before long.
When you call a office in x64, the manner and utilize of the registers follows a very specific convention. This dictates where the parameters to the role should go and where the return value from the function volition be when the function finishes. This is important so code compiled with ane compiler can be used with code compiled with some other compiler.
For case, take a look at this simple Objective-C code:
NSString *proper name = @"Zoltan"; NSLog(@"Hello globe, I am %@. I'yard %d, and I live in %@.", proper name, 30, @"my father's basement");
There are four parameters passed into the NSLog
function telephone call. Some of these values are passed as-is, while one parameter is stored in a local variable, and so referenced equally a parameter in the function. Yet, when viewing code through assembly, the computer doesn't care about names for variables; it just cares about locations in memory.
The following registers are used equally parameters when a function is called in x64 associates. Try and commit these to memory, as you'll apply these frequently in the time to come:
- Kickoff Argument:
RDI
- Second Argument:
RSI
- 3rd Argument:
RDX
- Fourth Argument:
RCX
- Fifth Argument:
R8
- Sixth Argument:
R9
If there are more than 6 parameters, so the program's stack is used to laissez passer in additional parameters to the role.
Going back to that unproblematic Objective-C code, you can re-imagine the registers existence passed like the following pseudo-code:
RDI = @"Howdy world, I am %@. I'm %d, and I live in %@."; RSI = @"Zoltan"; RDX = 30; RCX = @"my begetter's basement"; NSLog(RDI, RSI, RDX, RCX);
As before long equally the NSLog
function starts, the given registers will contain the advisable values as shown higher up.
However, as soon every bit the function prologue (the beginning department of a function that prepares the stack and registers) finishes executing, the values in these registers will likely change. The generated associates will likely overwrite the values stored in these registers, or only just discard these references when the code has no more demand of them.
This means every bit before long as you get out the start of a function (through stepping over, stepping in, or stepping out), you can no longer assume these registers will hold the expected values you want to detect, unless you actually await at the assembly lawmaking to meet what it's doing.
This calling convention heavily influences your debugging (and breakpoint) strategy. If you were to automate any type of breaking and exploring, you would accept to stop at the start of a function call in order to audit or change the parameters without having to actually swoop into the assembly.
Objective-C and Registers
Registers use a specific calling convention. Yous can take that same knowledge and employ it to other languages as well.
When Objective-C executes a method, a special C function is executed named objc_msgSend
. There's really several different types of these functions, just more on that later. This is the heart of message dispatch. As the kickoff parameter, objc_msgSend
takes the reference of the object upon which the message is being sent. This is followed past a selector, which is simply just a char *
specifying the name of the method being called on the object. Finally, objc_msgSend
takes a variable amount of arguments within the function if the selector specifies there should be parameters.
Let's look at a physical instance of this in an iOS context:
[UIApplication sharedApplication];
The compiler will take this code and create the following pseudocode:
id UIApplicationClass = [UIApplication class]; objc_msgSend(UIApplicationClass, "sharedApplication");
The first parameter is a reference to the UIApplication
course, followed by the sharedApplication
selector. An like shooting fish in a barrel fashion to tell if there are whatever parameters is to merely check for colons in the Objective-C selector. Each colon will represent a parameter in a Selector.
Here's another Objective-C case:
NSString *helloWorldString = [@"Tin can't Slumber; " stringByAppendingString:@"Clowns volition eat me"];
The compiler will create the following (shown below in pseudocode):
NSString *helloWorldString; helloWorldString = objc_msgSend(@"Tin't Sleep; ", "stringByAppendingString:", @"Clowns will eat me");
The commencement argument is an case of an NSString
(@"Tin't Slumber; "
), followed by the selector, followed by a parameter which is also an NSString
instance.
Using this cognition of objc_msgSend
, you lot can use the registers in x64 to help explore content, which you'll practice very shortly.
Putting Theory to Practise
You can download the starter project for this tutorial hither.
For this section, you'll be using a project supplied in this tutorial's resource bundle called Registers.
Open this project up through Xcode and give information technology a run.
This is a rather simple application which merely displays the contents of some x64 registers. It's of import to note that this application can't brandish the values of registers at any given moment, information technology can just display the values of registers during a specific function call. This means that you won't run across too many changes to the values of these registers since they'll likely have the same (or similar) value when the function to grab the register values is called.
Now that you've got an understanding of the functionality behind the Registers macOS awarding, create a symbolic breakpoint for NSViewController
's viewDidLoad
method. Remember to use "NS" instead of "UI", since you lot're working on a Cocoa application.
Build and rerun the application. Once the debugger has stopped, type the following into the LLDB console:
(lldb) register read
This will list all of the main registers at the paused state of execution. However, this is too much information. You should selectively impress out registers and treat them equally Objective-C objects instead.
If y'all recall, -[NSViewController viewDidLoad]
volition be translated into the following assembly pseudocode:
RDI = UIViewControllerInstance RSI = "viewDidLoad" objc_msgSend(RDI, RSI)
With the x64 calling convention in heed, and knowing how objc_msgSend
works, y'all can find the specific NSViewController
that is existence loaded.
Blazon the following into the LLDB console:
(lldb) po $rdi
You'll get output like to the following:
<Registers.ViewController: 0x6080000c13b0>
This will dump out the NSViewController
reference held in the RDI
register, which equally you now know, is the location of the commencement argument to the method.
In LLDB, it's important to prefix registers with the $
character, and so LLDB knows you want the value of a register and not a variable related to your scope in the source code. Aye, that's different than the assembly yous see in the disassembly view! Annoying, eh?
Notation: The observant among you might discover whenever you stop on an Objective-C method, yous'll never encounter the objc_msgSend
in the LLDB backtrace. This is because the objc\_msgSend
family of functions perfoms a jmp
, or spring opcode control in assembly. This means that objc\_msgSend
acts every bit a trampoline function, and once the Objective-C code starts executing, all stack trace history of objc\_msgSend
will be gone. This is an optimization known equally tail telephone call optimization.
Try printing out the RSI
register, which volition hopefully contain the selector that was called. Blazon the following into the LLDB console:
(lldb) po $rsi
Unfortunately, you'll get garbage output that looks something like this:
140735181830794
Why is this?
An Objective-C selector is basically simply a char *
. This means, like all C types, LLDB does non know how to format this data. As a result, yous must explicitly cast this reference to the data type y'all want.
Try casting it to the correct blazon:
(lldb) po (char *)$rsi
You'll now become the expected:
"viewDidLoad"
Of course, you lot can also bandage it to the Selector type to produce the same result:
(lldb) po (SEL)$rsi
At present, it's time to explore an Objective-C method with arguments. Since yous've stopped on viewDidLoad
, you tin can safely assume the NSView
instance has loaded. A method of interest is the mouseUp:
selector implemented by NSView
's parent class, NSResponder
.
In LLDB, create a breakpoint on NSResponder
's mouseUp:
selector and resume execution. If you can't remember how to do that, here are the commands you demand:
(lldb) b -[NSResponder mouseUp:] (lldb) continue
Now, click on the application's window. Make sure to click on the outside of the NSScrollView as information technology will gobble up your click and the -[NSResponder mouseUp:]
breakpoint will not get hit.
Equally soon as you let get of the mouse or the trackpad, LLDB will end on the mouseUp:
breakpoint. Print out the reference of the NSResponder
by typing the following into the LLDB console:
(lldb) po $rdi
You'll get something similar to the following:
<NSView: 0x608000120140>
However, there's something interesting with the selector. In that location's a colon in it, pregnant there'south an argument to explore! Blazon the post-obit into the LLDB console:
(lldb) po $rdx
You lot'll get the description of the NSEvent:
NSEvent: type=LMouseUp loc=(351.672,137.914) time=175929.4 flags=0 win=0x6100001e0400 winNum=8622 ctxt=0x0 evNum=10956 click=1 buttonNumber=0 pressure level=0 deviceID:0x300000014400000 subtype=NSEventSubtypeTouch
How tin can you tell it's an NSEvent
? Well, you can either look online for documentation on -[NSResponder mouseUp:]
or, you can simply use Objective-C to get the type:
(lldb) po [$rdx class]
Pretty cool, eh?
Sometimes it's useful to employ registers and breakpoints in order to get a reference to an object you know is alive in retentiveness.
For example, what if you wanted to change the front NSWindow to red, only you lot had no reference to this view in your code, and you didn't want to recompile with whatever lawmaking changes? You tin can simply create a breakpoint you can hands trip, get the reference from the register and manipulate the instance of the object as you please. Yous'll try changing the principal window to reddish now.
Notation: Even though NSResponder
implements mouseDown:
, NSWindow
overrides this method since it's a subclass of NSResponder
. You tin can dump all classes that implement mouseDown:
and figure out which of those classes inherit from NSResponder
to determine if the method is overridden without having access to the source lawmaking. An example of dumping all the Objective-C classes that implement mouseDown:
is epitome lookup -rn '\ mouseDown:'
First remove whatsoever previous breakpoints using the LLDB console:
(lldb) breakpoint delete About to delete all breakpoints, do you want to do that?: [Y/n]
So blazon the post-obit into the LLDB panel:
(lldb) breakpoint set -o -S "-[NSWindow mouseDown:]" (lldb) continue
This sets a breakpoint which will burn only one time — a one-shot breakpoint.
Tap on the application. Immediately after tapping, the breakpoint should trip. Then blazon the following into the LLDB console:
(lldb) po [$rdi setBackgroundColor:[NSColor redColor]] (lldb) continue
Upon resuming, the NSWindow will change to red!
Swift and Registers
When exploring registers in Swift, you lot'll hit two hurdles that make associates debugging harder than information technology is in Objective-C.
- First, registers are not available in the Swift debugging context. This means you accept to get any data y'all want and then utilize the Objective-C debugging context to impress out the registers passed into the Swift office. Remember that you can use the
expression -l objc -O --
command, or alternatively apply thecpo
custom command found in Chapter 8 of the book, "Persisting and Customizing Commands". Fortunately, theregister read
command is available in the Swift context. - Second, Swift is not as dynamic as Objective-C. In fact, it's sometimes best to assume that Swift is like C, except with a very, very cranky and snobby compiler. If you lot take a memory address, you demand to explicitly cast it to the object you wait it to be; otherwise, the Swift debugging context has no inkling how to interpret a memory address.
That being said, the same register calling convention is used in Swift. However, there's one very of import difference. When Swift calls a function, it has no need to use objc_msgSend
, unless of course you mark up a method to use dynamic
. This means when Swift calls a function, the previously used RSI register assigned to the selector will actually contain the function'south second parameter.
Enough theory — time to run across this in action.
In the Registers projection, navigate to ViewController.swift and add the following function to the class:
func executeLotsOfArguments(one: Int, two: Int, three: Int, four: Int, v: Int, half dozen: Int, seven: Int, eight: Int, nine: Int, ten: Int) { print("arguments are: \(i), \(two), \(three), \(4), \(five), \(half dozen), \(seven), \(eight), \(nine), \(ten)") }
Now, in viewDidLoad
, call this office with the advisable arguments:
override func viewDidLoad() { super.viewDidLoad() self.executeLotsOfArguments(one: ane, two: ii, three: 3, iv: 4, five: 5, vi: 6, seven: 7, viii: 8, nine: 9, ten: 10) }
Put a breakpoint on the very aforementioned line equally of the declaration of executeLotsOfArguments
so the debugger will stop at the very beginning of the function. This is important, or else the registers might go clobbered if the function is actually executing.
And then remove the symbolic breakpoint you assail -[NSViewController viewDidLoad]
.
Build and run the app, then await for the executeLotsOfArguments
breakpoint to terminate execution.
Once more, a skilful way to start investigating is to dump the list registers. In LLDB, type the following:
(lldb) register read -f d
This volition dump the registers and display the format in decimal past using the -f d
option. The output will wait similar to this:
Full general Purpose Registers: rax = 7 rbx = 9 rcx = 4 rdx = 3 rdi = 1 rsi = two rbp = 140734799801424 rsp = 140734799801264 r8 = 5 r9 = vi r10 = x r11 = eight r12 = 107202385676032 r13 = 106652628550688 r14 = x r15 = 4298620128 libswiftCore.dylib`swift_isaMask rip = 4294972615 Registers`Registers.ViewController.viewDidLoad () -> () + 167 at ViewController.swift:xvi rflags = 518 cs = 43 fs = 0 gs = 0
Every bit you can see, the registers follow the x64 calling convention. RDI
, RSI
, RDX
, RCX
, R8
and R9
concord your first six parameters.
You may likewise discover other parameters are stored in some of the other registers. While this is true, information technology'southward simply a leftover from the lawmaking that sets up the stack for the remaining parameters. Remember, parameters after the sixth i proceed the stack.
RAX, the Return Register
But expect — there's more! So far, you've learned how six registers are called in a function, but what near return values?
Fortunately, at that place is merely one designated register for render values from functions: RAX
. Become back to executeLotsOfArguments
and modify the part to return a String
, like so:
func executeLotsOfArguments(one: Int, two: Int, 3: Int, 4: Int, five: Int, half-dozen: Int, seven: Int, eight: Int, nine: Int, x: Int) -> Cord { print("arguments are: \(one), \(two), \(iii), \(iv), \(5), \(half-dozen), \(seven), \(eight), \(nine), \(10)") render "Mom, what happened to the cat?" }
In viewDidLoad
, change the function call to receive and ignore the String value.
override func viewDidLoad() { super.viewDidLoad() let _ = self.executeLotsOfArguments(one: 1, 2: 2, iii: 3, four: four, five: 5, six: 6, seven: 7, eight: 8, nine: nine, ten: 10) }
Create a breakpoint somewhere in executeLotsOfArguments
. Build and run again, and wait for execution to terminate in the function. Next, type the post-obit into the LLDB console:
(lldb) finish
This will finish executing the current function and pause the debugger over again. At this bespeak, the return value from the part should exist in RAX
. Blazon the following into LLDB:
(lldb) register read rax
You'll get something like to the following:
rax = 0x0000000100003760 "Mom, what happened to the true cat?"
Boom! Your return value!
Knowledge of the return value in RAX
is extremely important every bit information technology volition class the foundation of debugging scripts you'll write in afterward sections.
Changing Values in Registers
In order to solidify your understanding of registers, you'll modify registers in an already-compiled application.
Close Xcode and the Registers project. Open up a Last window and launch the iPhone 7 Simulator. Exercise this past typing the post-obit:
xcrun simctl list
You'll see a long list of devices. Search for the latest iOS version for which you take a simulator installed. Underneath that section, find the iPhone 7 device. Information technology will wait something similar this:
iPhone 7 (269B10E1-15BE-40B4-AD24-B6EED125BC28) (Shutdown)
The UUID is what you're after. Use that to open the iOS Simulator by typing the post-obit, replacing your UUID equally advisable:
open /Applications/Xcode.app/Contents/Developer/Applications/Simulator.app --args -CurrentDeviceUDID 269B10E1-15BE-40B4-AD24-B6EED125BC28
Make sure the simulator is launched and is sitting on the domicile screen. You tin can get to the dwelling screen by pressing Control + Shift + H. One time your simulator is set up, head over to the Last window and attach LLDB to the SpringBoard application:
lldb -n SpringBoard
This attaches LLDB to the SpringBoard instance running on the iOS Simulator! SpringBoard is the program that controls the home screen on iOS.
Once attached, type the following into LLDB:
(lldb) p/x @"Yay! Debugging"
You should go some output similar to the following:
(__NSCFString *) $3 = 0x0000618000644080 @"Yay! Debugging!"
Have a note of the memory reference of this newly created NSString
case as you lot'll apply information technology before long. Now, create a breakpoint on UILabel
'south setText:
method in LLDB:
(lldb) b -[UILabel setText:]
Adjacent, type the following in LLDB:
(lldb) breakpoint command add
LLDB will spew some output and go into multi-line edit mode. This command lets you add together extra commands to execute when the breakpoint you just added is hit. Type the following, replacing the retentiveness accost with the address of your NSString
from above:
> po $rdx = 0x0000618000644080 > proceed > DONE
Have a step back and review what you've merely done. You've created a breakpoint on UILabel
'southward setText:
method. Whenever this method gets hit, you're replacing what'due south in RDX
— the third parameter — with a different NSString
instance that says Yay ! Debugging !.
Resume the debugger by using the keep
command:
(lldb) continue
Attempt exploring the SpringBoard Simulator app and see what content has changed. Swipe upwards from the lesser to bring upwards the Command Centre, and detect the changes:
Effort exploring other areas where modal presentations can occur, as this will likely consequence in a new UIViewController (and all of its subviews) being lazily loaded, causing the breakpoint activity to be hitting.
Although this might seem like a cool gimmicky programming play a trick on, information technology provides an insightful look into how a limited knowledge of registers and assembly tin produce big changes in applications you don't accept the source for.
This is besides useful from a debugging standpoint, every bit you lot tin quickly visually verify where the -[UILabel setText:]
is executed within the SpringBoard application and run breakpoint conditions to discover the exact line of code that sets a particular UILabel
'south text.
To continue this idea, whatsoever UILabel
instances whose text did not modify also tells yous something. For example, the UIButton
due south whose text didn't change to Yay! Debugging!
speaks for itself. Perhaps the UILabel
's setText:
was called at an earlier time? Or maybe the developers of the SpringBoard awarding chose to utilize setAttributedText:
instead? Or peradventure they're using a private method that is not publicly available to third-party developers?
As yous can run across, using and manipulating registers tin can give you a lot of insight into how an application functions. :]
Where to Get From Here?
Whew! That was a long one, wasn't information technology? Sit down dorsum and accept a interruption with your favorite grade of liquid; yous've earned information technology.
You can download the completed project from this tutorial here.
So what did you larn?
- Architectures ascertain a calling convention which dictates where parameters to a function and its render value are stored.
- In Objective-C, the
RDI
annals is the reference of the callingNSObject
,RSI
is the Selector,RDX
is the starting time parameter and so on. - In Swift,
RDI
is the start argument,RSI
is the 2d parameter, and then on provided that the Swift method isn't using dynamic acceleration. - The
RAX
register is used for return values in functions regardless of whether you're working with Objective-C or Swift. - Make certain you use the Objective-C context when press registers with
$
.
There's a lot y'all tin do with registers. Attempt exploring apps you don't accept the source code for; it's a lot of fun and will build a proficient foundation for tackling tough debugging bug.
Try attaching to an application on the iOS Simulator and map out the UIViewController
s equally they appear using assembly, a smart breakpoint, and a breakpoint command.
If y'all enjoyed what y'all learned in the tutorial, why not check out the consummate Advanced Apple Debugging & Opposite Engineering science book, available on our shop?
Here's a sense of taste of what's in the book:
- Getting Started: Learn your way effectually LLDB and its all-encompassing list of subcommands and options.
- Python Power: Apply LLDB's Python module to create powerful, custom debugging commands to introspect and broaden existing programs.
- Understanding Assembly: Truly understand how code works at an assembler-level and how you can explore code in memory.
- Ptrace and Friends: Learn how to leverage ptrace, dlopen and dlsym to hook into C and Swift functions to explore code that you lot don't have the source for.
- Script Bridging: Extend the debugger to get in do almost anything you want, and learn how to pass in options or arguments to your debugging scripts.
- DTrace: Dig deep and hook into a function with a DTrace probe to query a massive amount of process information.
- …and more!
By the terminate of this book, you'll have the tools and cognition to answer even the well-nigh obscure question most your lawmaking — or someone else'south.
To gloat the launch of the book, information technology's currently on sale for $44.99 — that'south a $10 discount off the comprehend cost! Only don't expect too long, every bit the launch bargain is but on until Friday, May 19th.
If you have any questions or comments on this tutorial, experience gratuitous to join the word below!
Which Order Are Registers Used To Store Arguments,
Source: https://www.raywenderlich.com/615-assembly-register-calling-convention-tutorial
Posted by: greenabrount1980.blogspot.com
0 Response to "Which Order Are Registers Used To Store Arguments"
Post a Comment