To the current position, when the JIT code is executed (for example: Objective-c, Swift, C, and so on. The code is executed by your Python script), and you use a small number of APIs to execute the code.
For example, you use the Handlecommand method of Sbdebugger and Sbcommandreturnobject to execute the script. Sbdebugger handlecommand Direct output to stderr, At the same time you can control where the results of the sbcommandreturnobject end. Once executed, you will have to manually parse the returned output to the part you are interested in. Manual search from the output of the JIT code
It's a little ugly. No one likes the word type thing!
So it's time to introduce a new class in the Lldb Python module, Sbvalue, how he has made the output of the analytic JIT code simple.
Open the allocator project in the starter directory in this chapter. This is a simple application that can dynamically generate classes based on the contents of the input box.
Picture. png
This is a completed string passing the input box to the Nsclassfromstring function to generate a class. If a valid class is returned, it is initialized with the old Init method. Otherwise, an error is output.
Build and run the app on the iPhone 7 Plus Simulator. You do not need to make any changes to this program, and you will use Sbvalue to see the layout of objects in memory, as well as manual pointers through LLDB.
The Detour of memory layout
Really thank you for the powerful Sbvalue class, you will browse the memory layout of the three unique objects in the allocator application. You will start with a objective-c class, then browse for a swift class without a parent class, and finally browse a swift class that inherits from NSObject.
These three classes have the following three properties:
? A color property called Eyecolor
? A string (string/nsstring) attribute called FirstName.
? A string (string/nsstring) attribute called LastName.
Each of these classes uses the same initialization value. They are:
? The value of Eyecolor will be Uicolor.brown or [Uicolor Browncolor], depending on the language.
? The value of FirstName will be "Derek" or "Derek" depending on the language.
? The value of LastName will be "Selander" or @ "Selander" depending on the language.
Objective-c Memory Layout
You will first browse the Objective-c class, because it is the basis for how these objects are laid out in memory. Jump into the DSObjectiveCObject.h and look at it. Here is a reference for you:
@interface Dsobjectivecobject:nsobject
@property (nonatomic, strong) Uicolor Eyecolor;
@property (nonatomic, strong) NSString FirstName;
@property (nonatomic, strong) NSString *lastname;br/> @end
Jump to the implementation file DSOBJECTIVECOBJECT.M, and then look at and understand what happens when the Objective-c object is initialized:
@implementation Dsobjectivecobject
- (instancetype) Init
{
self = [super init];
if (self) {
Self.eyecolor = [Uicolor Browncolor];
Self.firstname = @ "Derek";
Self.lastname = @ "Selander";
}
return SELF;BR/>}
@end
When the code is compiled, the Objective-c class looks like it's actually a C struct. The compiler creates a pseudo-code similar to the following struct:
struct Dsobjectivecobject {
Class Isa;
Uicolor Eyecolor;
NSString FirstName
NSString *lastname
}
Notice the ISA variable for this class as the first argument. This is what a objective-c class is considered to be the magic behind a Objective-c class. In the memory layout of an object instance, ISA is always the first first value and always points to the class to which the instance belongs. The attributes are then added to the structure in the order that you implement them in your code.
Let's take a look at these operations through LLDB. Perform the following steps:
Make sure that Dsobjectivecobject is selected in the Uipickerview.
Click on the Allocate Class button.
Once the reference address is output to the console, copy that address to your clipboard.
Pause execution and then raise the LLDB console window.
Picture. png
An instance of Dsobjectivecobject has been created. Now you will use LLDB to explore the offset of this object's content.
Copy this memory address from the output of the console and then make sure that the PO memory address gives you a valid reference (for example. When you print out this address, you do not stop at the swift stack frame).
In my case, the pointer I get is 0X600000031F80. As always, your might be different from mine. Print out this address via LLDB:
(LLDB) PO 0x600000031f80
You should get the expected output from the following line:
<DSObjectiveCObject:0x600000031f80>
Since this can be used as a C struct, you will begin to explore the offset of this pointer content.
In the LLDB console, enter the following (replace the following pointer with your pointer):
(LLDB) PO (ID ) (0x600000031f80)
This line of code indicates that the pointer is an ID type and then it is dereferenced. This will access the ISA pointer for this object.
You should see these outputs:
Dsobjectivecobject
This is the description of the class, as we expected.
Let's look at this memory in a different way. Use the x command (aka examine, a command handed down from GDB) to jump to the starting position of the pointer and po it. Enter the following content:
(LLDB) X/GX 0x600000031f80
This line of command did the following things:
? Check this piece of memory (x)
? Print out the size of big-endian words (64 bits, or 8 bytes) (g)
? Finally, format it with 16 binary (x).
If, assuming, you just want to see the contents of the first byte in the binary file, you can enter X/BT 0x600000031f80 instead. This will be interpreted as checking (x), one byte (b) in the binary (t). This examine command is a good one in the easy-to-use commands when browsing memory.
You will see the following output (or at least, a similar output, although your values are different from my Ken):
0x600000031f80:0x0000000108b06568
These outputs tell you that the value contained in the memory address 0x600000031f80 is 0x0000000108b06568. Well, that's the value I get!
Go back to the task in our hands, take the address printed by the X/GX command and print out the new address with the PO command.
(LLDB) PO 0x0000000108b06568
Again, this will print out the Isa class, which is the Dsobjectivecobject class. This is an alternative way to print out an Isa instance, which may give you a deeper understanding of what's going on. However, it replaces one with two LLDB commands, so you will dereference the pointer and do not use the X/GX command.
Let's go deeper into the Eyecolor property a little bit. In the LLDB console:
(LLDB) PO (ID ) (0x600000031f80 + 0x8)
This line of instruction says "start with 0x600000031f80, add 8 bytes and get the contents of the pointer here." You will get the following output:
Uiextendedsrgbcolorspace 0.6 0.4 0.2 1
How do you know that I get 8 of the quantity? Try one of the following commands in LLDB:
(LLDB) PO sizeof (CLASS)
The ISA variable is the class type. So by knowing how big a class is, you can see how much space it occupies in this structure, so you can know the eyecolor of the shift.
Note: When we work with a 64-bit architecture (x64 or ARM64), pointers to all nsobject subclasses are 8 bytes. In addition, Class
this class himself is 8 bytes. This means that on a 64-bit architecture, you need to move only 8 bytes to switch between different classes!
There are some types that are hungry for different sizes, such as, int
short
bool
and other basic types, and on a 64-bit machine The compiler may be able to complement the predefined 8 bytes. However, there is no need to worry about this now, because it DSObjectiveCObject
contains only pointers to NSObject subclasses, Variables can be obtained along the class object isa
.
Proceed. Increase the offset by 8 bytes in Lldb:
(LLDB) PO (ID ) (0x600000031f80 + 0x10)
Now you've added another 8 bytes, in hexadecimal it's 0x10 (or 16 in decimal). The result you get will be @ "Derek", which is the value of the FirstName property. Add 8 more bits to get the value of the LastName property:
(LLDB) PO (ID ) (0x600000031f80 + 0x18)
You will get @ "Selander". It's cool, isn't it?
Let's look at what you just did with a list of charts:
Picture. png
You start with a base address that points to a dsobjectivecobject instance. In this example, the starting address is 0x600000031f80. You begin to dereference this pointer, which gives you the ISA variable, then you increment the 8 byte offset to get the next Objective-c property, dereference the address after the offset, map it to the ID type, and then output it to the console.
Exploring memory is interesting and indicates a way to see what is going on behind the phenomenon. This makes you more grateful for the Sbvalue class. But now is not the time to discuss the Sbvalue class, because you have two classes to browse. The first one is the Swift class without the parent class, and the second is the Swift class that inherits from NSObject. You will first see the Swift class without the parent class.
The memory layout of the Swift class without the parent class
Note: One thing to note in advance: Swift's API is still changing. This means that the following information may change before Swift's ABI is complete (stabilized). The time that the new version of Xcode is released may break the following section below.
It's time to browse the Swift class without the parent class!
In the allocator project, jump to the Aswiftclass.swift class and look at the code there.
Class Aswiftclass {
Let Eyecolor = Uicolor.brown
Let FirstName = "Derek"
Let LastName = "Selander"
Required init () {}
}
Here, you have a swift-styled object that equates to Dsobjectivecobject.
At one time, you can think of the Swift class as similar to the C structure of objective-c but there are some interesting different C structures. Look at the following pseudo-code:
struct Aswiftclass {
Class Isa;
uint64_t refcounts;
Uicolor *eyecolor;
struct _stringcore {
uintptr_t _baseaddress;
uintptr_t _countandflags;
uintptr_t _owner;
} firstName;
struct _stringcore {
uintptr_t _baseaddress;
uintptr_t _countandflags;
uintptr_t _owner;
} firstName;
}
Pretty funny, right? You still have the ISA variable as the first parameter. After the ISA variable, is a 8-bit reserved refcounts variable. This is somewhat different from the typical Objective-c object that does not contain these reference counters in this location. Note uint64_t This type, this type consumes 8 bytes of memory-even on 32-bit machines. This differs from the uintptr_t type, This type can be either 32-bit or 64-bit depending on the hardware of the machine.
The next is the ordinary uicolor.
Swift's string is a very interesting object. In fact, a swift string is a struct that contains the ASWIFTCLASS structure inside. When you look at the memory, each swift string contains three parameters:
? This is the actual address of the character data for string.
? The next step is to mix the length and the flags in one parameter; it can be Unichar or ASCII, or something crazy that I don't know how to explain.
? Finally, is a reference to its holder.
Because you are declaring these strings at compile time (with those let's), there is no need for the holder because the compiler will only need to apply the offset of the actual position of the string, because they are immutable.
This swiftstring struct actually makes assembly calling conventions quite interesting. If you pass a string to a function, he will actually wear three parameters (and use three registers) instead of a pointer to a struct containing three parameters (in a register). Don't believe me? When this chapter is over, check it yourself!
Go back to Lldb and jump to an object.
Use? + k Empty the console and continue running the application with LLDB or Xcode.
You will do the same thing on the Aswiftclass as the Dsobjectivecobject. Use the developer/designer "approved" Uipickerview and select Allocator.swiftclass. Remember, to correctly refer to a swift class (for example, nsclassfromstring and other similar classes), you need to use the name of the module as a prefix for the class name and separate the two with a period.
Click the Allocate class button and copy the memory address of the console output.
Picture. png
You will get some output similar to the following:
<Allocator.ASwiftClass:0x61800009d830>
In general, Swift hides pointers to description and debugdescription, but some sneaky things are compiled into the project, as you'll see later.
But now, grab the memory address and copy it to the Clipboard.
First use LLDB to make sure it is valid, po it a bit:
(LLDB) PO 0x61800009d830
If you get some different output with the following, you might be a little surprised:
<Allocator.ASwiftClass:0x61800009d830>
Although this is a pure Swift object, you can still get his dynamic description in the OBJECTIVE-C environment. This means that you can climb up to his inheritance tree to see his father Class!
(LLDB) PO [0x61800009d830 Superclass]
You will get a class with an interesting class name:
Swiftobject
You will see more information about this class later. Now, start jumping into memory to see the memory. Dereference the pointer to this address and verify for yourself that the first parameter is the ISA class variable:
(LLDB) PO (ID ) 0x61800009d830
You will get allocator.aswiftclass. Now check for a reference counter variable:
(LLDB) PO (ID ) (0x61800009d830 + 0x8)
You will get some output similar to the following:
0x0000000200000004
Obviously this address is not an objective-c address, because (ID) 0x0000000200000004 will point to a class if it is a valid instance/class. Instead, this is the only reference counter for the Swift class. Let's take a look at how these work.
Use LLDB manual retain this class:
(LLDB) PO [0x61800009d830 retain]
Press the UP ARROW button again to re-execute the previous command:
(LLDB) PO (ID ) (0x61800009d830 + 0x8)
You will get a slightly different number:
0x0000000200000008
Note that the last very important hexadecimal value jumps forward by 4 bytes, while 2^33 (yes, that is 0x0000000200000000) is the same value. See if release reduces the count of this reference:
(LLDB) PO [0x61800009d830 Release]
Similarly, press the UP ARROW key twice, and then enter.
(LLDB) PO (ID ) (0x61800009d830 + 0x8)
You will get a value that makes you very happy, the original value:
0x0000000200000004
Picture. png
This time try to retain this object two times, and then print out this reference count value:
(LLDB) PO [0x61800009d830 retain]
(LLDB) PO [0x61800009d830 retain]
(LLDB) PO (ID ) (0x61800009d830 + 0x8)
You will get a value like this:
0x000000020000000c
Each time retain, this value increases by 4. That looks like an addition, but in fact, it's only the lowest 2 bits that are not used and the reserved values increase by 1 each time. In other words, increase 0x100 every time.
Finally, release this object two times to balance the two retains:
(LLDB) PO [0x61800009d830 retain]
(LLDB) PO [0x61800009d830 retain]
Now that you've seen the ISA variable and the refcounts variable it's time to focus your attention on the lovely attributes on the Aswiftclass instance.
Clear the screen to refresh and increase your offset in the lldb.
(LLDB) PO (ID ) (0x61800009d830 + 0x10)
You will get the values in the kernel that represent Uicolor ' in the brown color:
Uiextendedsrgbcolorspace 0.6 0.4 0.2 1
Skip the other 8 bytes and start browsing the FirstName String struct:
(LLDB) PO (ID ) (0x61800009d830 + 0x18)
0x0000000101f850d0
As you can see in the pseudo-code structure, this is the actual base address of the Swift string class start. Essentially, this base site can be viewed as a C char or a C Unichar(useful for all emojis strings). So all you have to do is to specify its type correctly. Because Swift string "Derek"
Must be in the ASCII range, this address is indicated as the char* type to replace the ID:
(LLDB) Po (Char *) (0x61800009d830 + 0x18)
"Derek"
Now look at the _countandflags offset. Increase your offset to 0x20 and then return the indicated type to the ID and continue browsing. The ID is a good default type because it resolves the type of objective-c he can solve, and if it cannot parse it translates to a hexadecimal address.
(LLDB) PO (ID ) (0x61800009d830 + 0x20)
You will get the following output:
0x0000000000000005
Once again, this represents the flags and length. Because the length of "Derek" is 5, you get 5 in the last position of the hexadecimal number. The remaining 0 indicates that the flags are not applied (that is, the char is substituted in the Unichar format).
Finally, increase your offset and then cross the _owner of Swift string.
(LLDB) PO (ID ) (0x61800009d830 + 0x28)
This will extract nil because this objective-c is equivalent to 0x0000000000000000. As mentioned earlier, there is no need for "owner" because this string is created in the compilation eat.
There is no need to look at the LastName property behind. You already know how this works.
Advanced+apple+debugging (10)