DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Frameworks
  4. Swift 5.0 Method Internals Unearthed

Swift 5.0 Method Internals Unearthed

Follow along and learn how methods work at a low-level in the Swift language.

Christopher Lamb user avatar by
Christopher Lamb
CORE ·
Apr. 30, 19 · Analysis
Like (2)
Save
Tweet
Share
4.35K Views

Join the DZone community and get the full member experience.

Join For Free

So far, we've been looking at a simple program to try to see how methods are defined internally in compiled Swift 5.0 programs. In our simple example, essentially a hello world example printing from a small class, we have been able to find the class definition itself but we haven't been able to see where the methods are defined. We traced through the compiled program and found this:

__objc_class__TtC9swift_cmd7Printer_data:
0000000100002120 struct __objc_data {
  0x80, // flags
  16, // instance start
  32, // instance size
  0x0,
  0x0, // ivar layout
  aTtc9swiftcmd7p, // name
  0x0, // base methods
  0x0, // base protocols
  __objc_class__TtC9swift_cmd7Printer_ivars, // ivars
  0x0, // weak ivar layout
  0x0 // base properties
  }

But it doesn't have any associated method definitions. In Objective-C, there's a list of method descriptors that contain pointers to functions, so we expected to find the same thing here. We didn't.

So how are these methods stored?

There's a couple of key hints. From the main() function, we have this section of disassembly:

mov rcx, qword [rbp+var_28]
mov rsi, qword [rcx]
mov rdi, rax
mov qword [rbp+var_30], rsi
mov rsi, rdx
mov r13, rcx
mov rax, qword [rbp+var_30]
mov qword [rbp+var_38], rdx
call qword [rax+0x80]

If you trace through this, you have these assignments:

rcx <- [rbp + var_20]
rsi <- rcx
[rbp + var_30] <- rsi
rax <- [rbp + var_30]

What's interesting here is the use of offsets from the base pointer on the stack. We're not pushing and popping here. So how are these function addresses getting there? Time to look at LLDB.

To start up LLDB, I do a couple of things. First, I use an lldb-run file in my local directory with the following contents:

$ cat lldb-run
br s -n main
run

Usually I'll tweak those as I move through the program, learning what exactly it's doing. So let's start up LLDB, use this command:

$ lldb -s lldb-run swift-cmd 

Then, when in main() check out the disassembly:

(lldb) di
swift-cmd, main
-> 0 0x100001840 <+0>: push rbp
 1 0x100001841 <+1>: mov rbp, rsp
 2 0x100001844 <+4>: push r13
 3 0x100001846 <+6>: sub rsp, 0x38
 4 0x10000184a <+10>: xor eax, eax
 5 0x10000184c <+12>: mov ecx, eax
 6 0x10000184e <+14>: mov dword ptr [rbp - 0xc], edi
 7 0x100001851 <+17>: mov rdi, rcx
 8 0x100001854 <+20>: mov qword ptr [rbp - 0x18], rsi
*9 0x100001858 <+24>: call 0x1000018d0 ; type metadata accessor for swift_cmd.Printer
 10 0x10000185d <+29>: mov r13, rax
 11 0x100001860 <+32>: mov qword ptr [rbp - 0x20], rdx
*12 0x100001864 <+36>: call 0x100001a60 ; swift_cmd.Printer.__allocating_init() -> swift_cmd.Printer
 13 0x100001869 <+41>: mov r8d, 0xc
 14 0x10000186f <+47>: mov esi, r8d
 15 0x100001872 <+50>: mov qword ptr [rip + 0x9af], rax swift_cmd.printer : swift_cmd.Printer ; 0x100002228 swift-cmd.__DATA.__common
 16 0x100001879 <+57>: mov rax, qword ptr [rip + 0x9a8] swift_cmd.printer : swift_cmd.Printer ; 0x100002228 swift-cmd.__DATA.__common
 17 0x100001880 <+64>: lea rdi, [rip + 0x599] "Hello World!" ; 0x100001e20 swift-cmd.__TEXT.__cstring
 18 0x100001887 <+71>: mov edx, 0x1
 19 0x10000188c <+76>: mov qword ptr [rbp - 0x28], rax
*20 0x100001890 <+80>: call 0x100001d66 ; (__TEXT.__stubs) Swift.String.init(_builtinStringLiteral: Builtin.RawPointer, utf8CodeUnitCount: Builtin.Word, isASCII: Builtin.Int1) -> Swift.String
 21 0x100001895 <+85>: mov rcx, qword ptr [rbp - 0x28]
 22 0x100001899 <+89>: mov rsi, qword ptr [rcx]
 23 0x10000189c <+92>: mov rdi, rax
 24 0x10000189f <+95>: mov qword ptr [rbp - 0x30], rsi
 25 0x1000018a3 <+99>: mov rsi, rdx
 26 0x1000018a6 <+102>: mov r13, rcx
 27 0x1000018a9 <+105>: mov rax, qword ptr [rbp - 0x30]
 28 0x1000018ad <+109>: mov qword ptr [rbp - 0x38], rdx
*29 0x1000018b1 <+113>: call qword ptr [rax + 0x80]
 30 0x1000018b7 <+119>: mov rdi, qword ptr [rbp - 0x38]
*31 0x1000018bb <+123>: call 0x100001d84 ; (__TEXT.__stubs) swift_bridgeObjectRelease
 32 0x1000018c0 <+128>: xor eax, eax
 33 0x1000018c2 <+130>: add rsp, 0x38
 34 0x1000018c6 <+134>: pop r13
 35 0x1000018c8 <+136>: pop rbp
*36 0x1000018c9 <+137>: ret

We want to set some additional breakpoints so we can understand what's going on. First, let's set them at the call on offset 113:

mov qword [rbp+var_38], rdx
call qword [rax+0x80]
mov rdi, qword [rbp+var_38]

We want to stop at the MOV opcode prior to the call. If we do, and we examine the contents of the RAX register, we see that they point to the top of the class definition structure at address 0x1000021a0. If we add 0x80 to this address, we have 0x100002220. The contents in this pointer are 0x100001c40, the address of the _$s9swift_cmd7PrinterC11printString7messageySS_tFfunction.

There's a block of function pointers in the program that are easy to miss. If we look at the address 0x100002220 in the disassembled program, we can see the address 0x100001c40, the pointer to the _$s9swift_cmd7PrinterC11printString7messageySS_tF function.

To wrap this up, the function pointers are stored next to the class definition structure, in what seems to be a table of other class data and function pointers. This is a bit more clear in IDA, which shows this at the bottom of the table:

__data:00000001000021F8 dq offset _$s9swift_cmd7PrinterC12str_to_printSSvg
__data:0000000100002200 dq offset _$s9swift_cmd7PrinterC12str_to_printSSvs
__data:0000000100002208 dq offset _$s9swift_cmd7PrinterC12str_to_printSSvM
__data:0000000100002210 dq offset _$s9swift_cmd7PrinterCACycfC
__data:0000000100002218 dq offset _$s9swift_cmd7PrinterC8printMsgyyF
__data:0000000100002220 dq offset _$s9swift_cmd7PrinterC11printString7messageySS_tF

It'll be interesting to see if this is an optimization or an extension of the Objective-C class definition structures that's currently undocumented. But for now, I think we've looked enough at Swift method implementations — there's plenty of Swift primitives that don't have Objective-C analogs, or are equally as core to the language, like Protocols.

Swift (programming language)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • A Simple Union Between .NET Core and Python
  • Kotlin Is More Fun Than Java And This Is a Big Deal
  • Implementing Infinite Scroll in jOOQ
  • Hidden Classes in Java 15

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: