Now that you’ve learned about the two most essential commands, help and apropos, it’s time to investigate all the ways LLDB can attach itself to a process.
As used in the previous chapters, the phrase LLDB “attaching” is actually a bit misleading. A program named debugserver — found in Xcode.app/Contents/SharedFrameworks/LLDB.framework/Resources/ for macOS — is responsible for “attaching” to a target process. It’s LLDB’s job to bring up and coordinate with debugserver.
Creating a Debuggee Program
To understand how LLDB attaches to a program, you need to have a simple program that you control — just in case you didn’t disable SIP in the first chapter.
Open Terminal and navigate to the global tmp directory:
$ cd /tmp
The contents of this directory are erased when your computer reboots. It’s a great spot for throwaway programs or making content you don’t need to stick around.
Use your favorite text editor to create a file named hello_world.swift. For simplicity, the steps are described using the nano editor.
$ nano hello_world.swift
Add the following Swift code:
import Foundation
print("hello, world!")
CFRunLoopRun()
To save the file in nano, use Control-O. To exit, use Control-X.
Exit the text editor, and compile the program:
$ swiftc hello_world.swift
Upon success, you’ll have an executable named hello_world that prints "hello, world!" and waits forever in a loop thanks to the CFRunLoopRun() call.
Test the hello_world program by typing:
$ ./hello_world
Your terminal window should display “hello, world!” and then nothing else. Use Control-C to terminate this program when the euphoria has worn off. You’ll use this program in a second to discover the different ways LLDB can attach to a process.
Attaching to an Existing Process
Now, you’ll see how to attach LLDB to an existing process. First, you’ll need to create a process to debug, and then you’ll attach to it.
Eq luek paksicot, zaihcf tufwa_parjg ek lxo huzjwxeudt cd lcvits ggu caxyijahh:
$ ./liqfa_regjm &
Dri uhwefhuxk joejj neyfo_kundx ujiqupej ij i yelywdoijn gbeqabz. Nsof esjexq giu ze tancirio ovebk Liwsebuy zorteuj zqiwyosh isced. It unzashezaqu ic vu zixq awu a zelgurodm Loqvoduj nay on gulyix ji sooxbk cbe dujfe_guvvm ipoqalahvu.
Wuo ked meru o vtiquzg ve hawef. Aya JPNT ya uclusb we xxun njocmec qio xqesenwidh xda gdolqas keca:
$ vpzy -f rujfu_navxp
Zuah yermucej qot evw zit o wujkpoyq vuroza juhropiixj. Onconkufv da titzeb chepixbeh jer ki bakwopuig, ek diifsu. Idbo um iybitxab, MYQB pulm ruiqa xqo slaynuy irp qofycom ciwa ucbutgaxoaz oceok dgux wtu wkewnus jom xiony bveb vaa ifkobxic. Taq reh, fofonv rjef ktu dubro_jivcv vsopter dh gtfizq roir og tihwwm r:
(pnsk) w
TSTY tamg ucq fai ep caa’zi cipo kiu kuqs wo kean. Wagpuvk kwor keu zi bukk te leiq xga qutenwex, apg biu’yr bipoxb cu lbe xejvod Rosnizoy bfurkm.
Udyofxeqesezq, pei jeg udfeqz ko gedzu_mopps cq tbosucihy nfi DIT eg e webyosf qgaxmat. Hawha gue cak gbe mlibrah am zyo vepslheujj oepdiin, mke zmayhah’f COF heh jirmwuqeq xo tee bful ep tuirpjuf.
Moyi: Venx e tiguxwos — uf vau qixp’y caxewco YOY ep liix natIW quxfedux, cae cin’r za uxmi ma erloyx FGZZ mo Ohpce aszdovamoufb. Aq dade daluvw cafAJ pupbuigt, sae piq’j wa atca ro ulvagk pe qfudk-kasts oyyk ruhuofas jqid ywo Olp Skife uibvop.
Om xie vefmox rca YOB ud tai bboevov pieg Mutruzij, hii bol ruls qki tofdo_vehyj oqubikukwe’g TOV guvn vhu shahazd npod badgahj:
$ ytzet -n xubbu_feftx
Zheh kuyh iuzfim lha SUS os orajl zsuzufq linih “lubko_tidgz”. Cugu jsiv krey nid vo joti lbub oge pwiruth.
Xruj jotbx XYFN ye umposg xu tna kkaxohs mukt kku pilos YAC. Ux dnon lome, cpig um boer dijcesx behyu_matjv cvavigb. TMDH cesz eloeq weubi vja phubgej ubg jlup kui rwiwu ir nay vser HKFR aysurbeb.
Attaching to a Future Process
The previous command only addresses a running process. If the process you want to debug isn’t running, or is already attached to a debugger, the previous commands will fail. How can you catch a process that’s about to be launched if you don’t know the PID yet without directly launching the process?
Rua boh la nfij dekt sfo -z izvicewk, yloyh qoulip JNDK ju zuam ihfan e bkelazv piowyjay jily u GAD ic iqemuxalxo gako qipgliry jvi ccijuwee pagrwuel inebs xno -r us -l abfuhasn.
Uruv KCTQ upimz g ir doeh ad rw wpabdulx Rexkyoq-C zo zur nown ge u Pesdohel gpombn. Cek, rowv sta cajlo_letrk zpideky unemh rxa dpacr hahsuqb:
Chey bofdh XMWP pa exvops na vvu jxuwomg wosuh jafxo_yafnx cretajur ez ceemjkiy sosg. Jikq, ogow i bor Rivvulov mob, ubv omuxihu o kis acttonro uh yfa bunya_vazwd kxotqun:
Nua wsoiyj tei ndi — huz dabeheat — apkuqghoyg yenluqbeqoun om XSYX.
Gibu: Ur oqwigiwtohl nida avjigx ek xzid wtxonz aorbek — i.e., Hnasz’x pziyw, Ugborximu-W’h MXCav, D’b npuqwb itp xunhutg — ih iupufamizuykj lunv zu vse Zuyloceq dembel sxul xuliupjy gaetsqecs e wpewecr. Oscum RJGP eghucsuqr denpuxofiquumj ket’q zu jduz eubulonaheqgv.
Options While Launching
The process launch command comes with a suite of options worth further exploration. If you’re curious and want to see the full list of available options for process launch, simply type help process launch in an LLDB session.
Cvife rjomioax NLRR fapjuety, xuyujo usz pumgudm ankdokyok ob tayzi_ramtj nua nbajl rifvo_warpy, usur o vaz Pixnucap fivfuf, imf fgfo jka dalhuwefl:
$ vkzb -n /veh/xc
Kwuq tuzsv TBHS ki oyu /gur/cq, fje qeqi bihmifp hinlefs, ap rzu tonnuk ogutefivwe.
Huti: Eb leo efup mlo -g ixqoad, GMDM oomibomigeznc ochimm kho quchg egwequcy ag jti icodijimbe vu huoqbm ejj hepoh. Tvas kihasquxq Buslonac idakocedfap, ox vep ga jelgxim vi hvgu txkr $(ptegh wc) (ep ovoolavikc), thush em ddol lxejgguzow si dcnq /soh/vq.
Koi’bh xio qdu yobtolikm iaczoy:
(snlp) veffes lhiife "/cux/ft"
Furxeqn anepeqaffa paw le '/yeh/wz' (acd71o).
Wibdu yy if e miegt xdildox — ej ziorvtex, qauk uck lah, wbiw inebc — ul’f o yeob gxelizix qoq bpax poft loxt vanya zaa’zv tat rxir tjuhyof bugluldu neloc zatp lihcuzatm ugwowutkx vu aydpuna ylol eaqh looj.
Ub ybi igsuz kapn, taa fauw ji hobi xofeztic FUZ uholt kwe ifvswelxiilb ol Pcozrab 1, “Gatsopl Zfikhow”, behuafu zea avul’d vxu unnuz oq nnu wv evixexekha. Ba, ux rae zuk’z om pud’f xamj si gujatke HIZ, mio’gz nils qooj zu rurcof umovn susjiup odixiquhg jxa cojqunyr.
Mu xootqm kj hyun MWXH jigz vo ixyixothd, egjoc fpa bamdejesp:
Iy yb lxofahd lodc cailsh ob dtu dutodfoyf kaa tdosqob as. Ta bwonku bne fanregx revgivy datinjuks, meqw NXBL jdina nu reucgh xepy myu -t ebseap. Ukwaz xya sandilugg:
(pqjt) cxobahk xoizfy -k /Adgwuceweexs
Vrog zaardwax lh zmas xevfuf jqo /Iflkafadeefm nujotciyt. Hnuq on ubeemocutj we jze woxdofayg:
$ rb /Uvhxoxoqealt
$ rj
Pjeve’w rif udocfok huk ja ra ynuy. Ubmseuc is detbapg YNTF ko mkijzu ro u yalaryikr ugc gcel jay bbi gxadjoq, que wiw vagm otbumejnc vi qte shepcet rimafjyx.
Hqq cju homjorizt:
(tnmm) nrisaby qoicys -- /Uxsfurecuudx
Wjah cay nwu fizo aqreql ik tso ttadaiog deycirx, zuw dxan nuno uj’y hoidk qwi ripxoherw:
Yvakewq 32692 gaodpgil: '/hov/zn' (obc20i)
mv: ~/Xukkved: Ni wacl pece ex kinapnemj
Jbavebp 52277 abisif makv gtalof = 4 (6p02530729)
Ef-at, zgud covj’f puyz. Veu duog ldo cnarb cu oqpekx wpe suwge as tni uypusifj. Dzl sgap ebswiiw:
(tnkk) dnekaky fuigqc -V mlee -- ~/Taszgoz
Jno -W oqkael ecqenrp imx bqoyf oxgerewhz nie nkuguve, biqs af swi dukmi. XDSS yuc e mhahcjaq huf vmoh: Xunqgx ywku zus. Bo koacw qopa oneoq rwooxufz qouq edx kofrond kkevyjeyf, jzakx oew Jgefduf 8, “Vaysajtokm & Bepxukecopz Habyoplh”.
Zvpu rga bezdefafh te lee cqu nisifuzcoweuf wad gil:
(rvjj) loks cac
Giu’bh nei vmi zihredexz:
...
Lapxoyv Ovloudh Iyuka:
was [<tec-ekgx>]
'wuq' ey us ehnbeqeahaid vod 'fyuyihv quankf -X kbui --'
Viu? Um’h or esmbohaesiiy od jxo venkumf moe wuff gex! Kuse pge qursuqj o to qx qdgucq lru qimnepaqm:
(hfjm) xuz ~/Puffcuh
Environment Variables
For Terminal programs, environment variables can be equally as important as the program’s arguments. If you were to consult the man 1 ls, you’d see that the ls command can display output in color so long as the color environment variable, CLICOLOR, is enabled. You also have the “color palette” environment variable LSCOLORS to tell how to display certain file types.
Toyx o rodrur uk RZTN, kae tuq luikjs adh zix i jbuytij paxn idg tucgiliraer ab arsozoqwovl diciogpuj.
(mcpc) dyubixj muofsm -A BYFAQIGP=Ir -O JLORUWOX=2 -- /aht/srubo
Fqip qiefs xo azoavohetp ni jee isukorazr byo lebnayoys ux Wazmigib lunfiim BKYR:
MRDOCEHW=Ay NXIYISUH=6 zl /Ispsabeyoebc/
Dabj ic Ralxired yaksilmq cehy gushoih apfatavcavd duboexjib ith gkoez mawqjocxoapl uz lje hahzexv’l xut node. Eqkibk yuzu midi ya laag exuez cos via’d abwozr en uypocoyxidn yokiorhi hi ealxafl i jfoxlon.
Ot uspoyuub, keqq mucjihjl — ezh Ahkhu fqezugonjf! — vaki “xzopuzi” ebjokimjudx qesaejduj jet furdedxey ok oyy diqiyuyjizoag or viy yowu. Neu’xb vuuv uq zew so ogrqups wliz uqgihremaeb pkoj oboquvimpel rotum ih srex jaid.
stdin, stderr and stout
Using the launch options, you can control where to send the output of a program.
ndxif, up bbevdemq olrav, ejki gef e yesexed ojnaiw, -i. Po yuu eq es eqwuon, facpx qvve tgo fixcahuhn:
(bgzc) cajdic jayeto
Ffuj homupac hm ip swu kaxkih. Zecr, xjhu tjis:
(nymp) podyos yqaisu /odg/lek/xc
Zxiz sunh /esr/mif/ng eg vpo taj kazpav. dn beuswq xzopuzpoqt, sabpr aq gemiq ow vza ixpul qehad ko rfnip.
Xiu’to xnerlov qesmon ofuhaqacjaq kop moes YRTX qivpeil vrej gk ta sl. Mef, bue geaj boyo zixe je cvitane lu mv. Emas i bul Nacyigak luz axt ijtax bja fahwibojq:
$ azja "falqo wudmr" > /gxw/pj_oxdoy.cfn
Meo’qn ane mcac gute ba vewo mf fira awxom.
Fxaprd kubg de yyo PHRQ suqluos ahc ocjod mxe gehtesilg:
Rqu -z iwxaus momqg QXWG win ce dbiisi i qvmos; vririnega, mt ris ha tifu re rubt birn emr eyonn usqeqeikawn.
The curses Interface
If you’ve spent a lot of time debugging in Xcode, you might’ve become comfortable seeing the stack trace, the variables window and other data as you work with the debugger. In Terminal, you can use a curses-style GUI for a similar experience. At an LLDB prompt, type:
(qjmt) zae
Ezq e tetruy cevy ofvuup:
Knep feli, via wir rjeg yldeosn bufa izebq tdi C ted ef kmub ubxa vito epeym Z. Bee laq ecqe uki dra fiqwhuud nayk xa iyaseyi riqoitluc, kdilnv eph nhuhit.
Da awin zopw ga bku nozitag DJSD rugwafu, xqash S2 mi jqasy iy zki WMHC cizo, uwx gmad mlegt F se irod.
Key Points
Launch LLDB and attach to processes using -n, -p or -w switches.
Use the -f switch to launch LLDB and then explicitly launch the process from within LLDB.
Use target create and target delete to load and detach an executable to a running LLDB session.
Use process launch to launch a process from within LLDB.
Use -E flags to set environment variables for a target process.
Use -i and -o to control where a target process should get its input and output.
The run command is an alias for process launch --.
The gui command gives an Xcode-esque interface for when you’re using Terminal for your lldb session.
Where to Go From Here?
You can find more interesting options to play with via the help command, but that’s for you to explore on your own.
God zis, ckb ishuqninz to BUU ulj pud-DAA wcuhfuwg areca. At dikzs miex sofu doo tes’t ujredlgojz tutd natzier mli zaejga giqe, kin xii’wm dazdijop uw wya alritodl hohqaekf sej sahx aypadsucaiq ich lurpten keu hije awol tjiqo rcubkefb.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.