forked from illumos/dev-guide
-
Notifications
You must be signed in to change notification settings - Fork 0
/
anatomy
2842 lines (2347 loc) · 101 KB
/
anatomy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# Component Anatomy, Creation and Modification
This section of the developer's guide covers everything that you need to know
about creating new commands, libraries, and kernel modules. Each component
listed here is independent, so feel free to jump ahead to one of the components
that you are interested in.
## Commands
Commands comprise the components of the system that the user is most familiar
with. These include all of the traditional Unix shell commands such as ls(1) and
grep(1), management tools like vmstat(1) and ifconfig(8), and illumos specific
tools like zfs(8), dtrace(8), and prstat(8).
Each directory in `usr/src/cmd` contains both the source code and Makefiles to
generate one or more programs which end up installed in various locations in the
proto area, the two most common locations for commands are `/usr/bin` and
`/usr/sbin`. In addition, userland daemons, such as dlmgmtd(8) and smbd(8),
are also found under `usr/src/cmd`. SMF manifests for services that run these
daemons are found inside of the source directories for a command.
### Handling Multiple Architectures
By default, most commands are 32-bit executables for the target architecture
you're building (eg. sparc or intel). For most binaries it doesn't matter
whether they are 32-bit or 64-bit. For example, the venerable command
yes(1) or cal(1) functions identically whether it's a 32-bit or
64-bit binary. This also comes from the traditional multilib configuration that
illumos has used across various compiler tool chains. The default architecture
for the compiler is 32-bit and a user can ask the compiler for a 64-bit binary
instead.
Some commands have code which varies based on whether the program is running on
an x86 or SPARC processor. They may contain plugins which only target x86. For
example, devfsadm(8) needs to builds an x86 specific plugin for devices which
do not occur on SPARC systems.
There are a third set of commands which need to execute a version depending on
the architecture of the kernel or of a process which is being debugged. Examples
of commands that fall into this bucket are ps(1) and mdb(1). To help
make this invisible to the user, illumos uses a command called `isaexec` which
is based on the isaexec(3C) library call. Programs that need this behavior
hardlink themselves to isaexec and place architecture specific copies of them
into the architecture specific directories. Take dtrace(8) as an example. A
32-bit i386 copy of the program is located in `/usr/sbin/i86/dtrace` and the
64-bit version in `/usr/sbin/amd64/dtrace`. When a user runs `/usr/sbin/dtrace`,
`isaexec` makes sure that the appropriate version for the architecture is run.
### Parts of a Command Directory
Among all of the different components in the system, commands are built the most
inconsistently. Part of this is due to the wildly different nature of commands
and the difference in complexity between them. At its simplest level a command
directory contains just two files: a source file, usually written in C, and a
Makefile. A good example of this is the cal(1) command. It's directory
looks like:
```
$ ls cmd/cal
Makefile cal.c
$
```
If you go ahead and build cal via `dmake` and bldenv(1ONBLD) you'll see the
program has been built and lands in the top level `cal` directory:
```
$ dmake
...
$ ls
Makefile cal cal.c
$ file cal
cal: ELF 32-bit LSB executable 80386 Version 1, dynamically linked,
not stripped, no debugging information available
$
```
#### Multiple Architecture Commands
The next most common set of commands are also single source file commands;
however, instead of building just the default 32-bit target, they need to build
both a 32-bit and 64-bit target. A good example of this is the gcore(1)
command. It consists of just one source file, but contains a few more
directories and Makefiles. At it's top level the gcore directory looks like:
```
Makefile gcore.c sparc
amd64 i386 sparcv9
```
Here the top level Makefile's main purpose is to invoke `make` recursively on
the subdirectories for the machine and its 64-bit architecture. The architecture
specific Makefile generally takes care of invoking the actual commands that need
to run. Often times, a great deal is shared between all the different
architectures, for example specific `CFLAGS` or library that need to be linked.
In such a case, you'll also find a file named `Makefile.com` in the command's
top level directory. Inside of each of the architecture subdirectories is a
single Makefile:
```
$ ls i386 amd64 sparc sparcv9
amd64:
Makefile
i386:
Makefile
sparc:
Makefile
sparcv9:
Makefile
```
Unlike with cal, the output for each architecture does not end up in the top
level directory. If it were, you'd quickly clobber the work of all the others.
Here, if you run `dmake` then it will build in the 32-bit and 64-bit
architecture that you're build is targeting. You can also cd into an individual
architecture directory eg. `cmd/gcore/amd64` and run `dmake` to just build that
one target. Generally, though, you'll want to stick to the top level.
#### Multi-source File Commands
Building up from our previous examples, the next thing that it makes sense to
look at is something with multiple source files. A good example of this is
prstat(8). The main difference between commands with just a single source
file and ones with multiple are the differences in the Makefiles. We'll go into
more detail with that a bit latter on as we demonstrate how to create something
that fits these different models. If you look at the top level directory for
`prstat`, you'll notice it looks somewhat famliiar.
```
$ ls cmd/prstat
Makefile prfile.c prstat.c prtable.h
Makefile.com prfile.h prstat.h prutil.c
amd64 prsort.c prstat.xcl prutil.h
i386 prsort.h prtable.c sparcv9
$
```
`prstat` uses the convention mentioned previously around `Makefile.com`. In
addition, its `Makefile.com` also defeines several make targets. This is used to
drive the compilation of the targets. As we described earlier, we're using
specific calls to the macros `$(LINK.c)` and `$(COMPILE.c)`. The use of these
directly is special to commands as there is no current centralized command
format. You may also be asking what is the deal with the `prstat.xcl` file. It
exists as a part of internationalization.
If you peak inside one of the target specific directories, you'll find all of
the object files as well as the generated objects. For example, if we poke
inside of the amd64 directory we see both the `.o` files and the `prstat`
binary.
```
$ ls cmd/prstat/amd64
Makefile prfile.o prsort.o prstat prstat.o prtable.o prutil.o
$ file cmd/prstat/amd64/prfile.o
cmd/prstat/amd64/prfile.o: ELF 64-bit LSB relocatable AMD64 Version 1
$ file cmd/prstat/amd64/prstat
cmd/prstat/amd64/prstat: ELF 64-bit LSB executable AMD64 Version 1, dynamically linked,
not stripped, no debugging information available
$
```
The same rules for building individual architectures versus all of them hold
true here as well. The `make` targets at the top level descend into all of the
subdirectories while the individual ones target just that specific architecture.
#### Multi-command Directories
Some directories are entire worlds unto themselves. The idea here is that you
have a top level component that has multiple sub-components in the form of
additional commands and sometimes even libraries. For example, rather than
having one directory in `cmd/` for `smbd`, `smbadm` and `smbstat`, they are instead
all contained within the `cmd/smbsrv` directory. This allows for the directory
structure to help impart and allow for more sharing between the directories.
The general strategy here is very similar to that for handling multiple
architectures where we invoke `make` recursively. In this case we invoke `make`
for each of the different directories and then inside of each of them you likely
have the familiar multiple architecture dance. This is just another layer of
indirection that allows for sharing common makefiles between different
components. Let's look at the contents of the `smbsrv` directory as an example:
```
$ ls cmd/smbsrv
dtrace Makefile.smbsrv.defs Makefile.subdirs smbd
Makefile Makefile.smbsrv.targ smbadm smbstat
$
```
Here the files `Makefile.smbsrv.defs` and `Makefile.smbsrv.targ` provide
definitions that are shared amongst all of the different commands. And if you
look inside one of these subdirectories you'll find that they look similar to
what we've seen elsewhere:
```
$ ls cmd/smbsrv/smbd
Makefile smbd_doorsvc.c smbd_nicmon.c smbd_vss.c
eventlog.dll smbd_join.c smbd_opipe_doorsvc.c svc-smbd
server.xml smbd_logon.c smbd_share_doorsvc.c
smbd.h smbd_main.c smbd_spool.c
```
On the far extreme end of these multi-command directories is the `sgs`
directory. `sgs` stands for the Software Generation Suite, which comes from Unix
System V. This contains everything related to the link-editor ld(1),
the runtime link-editor ld.so.1(1), tools like elfdump(1) and
nm(1), and libraries that are private to `sgs` such as libelf(3LIB).
Just as the `smbsrv` directory contains multiple makefiles to help share and
drive the build process, there are several for `sgs`. For example if we look
into the directory we see:
```
$ ls -F cmd/sgs
0@0/ error/ libld/ packages/
Makefile gprof/ liblddbg/ prof/
Makefile.com include/ libldmake/ pvs/
Makefile.sub lari/ libldstab/ ranlib/
Makefile.sub.64 ld/ librtld/ rtld/
Makefile.targ ldd/ librtld_db/ rtld.4.x/
Makefile.var lddstub/ link_audit/ size/
ar/ ldprof/ lorder/ symorder/
crle/ lex/ m4/ test/
dump/ libconv/ mcs/ tools/
elfdump/ libcrle/ messages/ tsort/
elfedit/ libdl/ moe/ unifdef/
elfwrap/ libelf/ nm/ yacc/
$
```
So while `sgs` is in the command directory, it does end up containing a few
libraries, some of which are private to the implementation of the commands. All
in all, the `cmd` section is the one that has the most flexibility there.
#### Building Native Programs
One thing that various components need to do is build a program that needs to
run on the build host to help aid in the construction of the actual program. An
example of this is done as a part of building nawk(1). When building a
native program it needs to be extra careful to actually build against the build
system's libraries and headers as well as using the native compiler as opposed
to the one that builds programs for the target system. As an example, here's a
part of `nawk`'s `Makefile.targ`:
```
maketab: maketab.c
$(NATIVECC) -O maketab.c -o $@ $(LDLIBS)
proctab.c: maketab
rm -f $@; ./maketab > $@
```
What we see here is that we're explicitly using the `NATIVECC` macro. In
addition, it's important to make sure that the LDLIBS for the `maketab` program
do not point into the general proto area and can only point to the build
system's libraries and headers.
#### Third Party Software
Third party software is included in various parts of commands. As a part of
that, there are two different files which are added to describe this state. If
we look at the `compress` command we see:
```
$ ls cmd/compress
Makefile THIRDPARTYLICENSE.descrip inc.flg
THIRDPARTYLICENSE compress.c
```
Here the file `THIRDPARTYLICENSE` contains the contents of the license itself.
Where `THIRDPARTYLICENSE.descrip` describes what components the license covers.
### Adding New Commands
Adding new commands is relatively straightforward. There are two different parts
to adding a new command. The first is the actual source code for that command
where as the second is related to hooking it into the larger build system. To
help illustrate this we're going to start with a simple example of a new command
called `gethrtime` which uses the wonderful library function
gethrtime(3C).
#### Writing the Command
To get started we're going to do all of this work from inside of a `bldenv`
environment. If you find yourself rusty, review the [Workflow
section](./workflow.html). To start off with, we need to make a new directory
for our command, copy the prototype files, and then write the new ones. Because
our command is called `gethrtime` we're going to name its directory simply
`gethrtime` and the code will all be inside of a single C file called
`gethrtime.c`. Similarly, because we only need one makefile for this as we're
only building the native target, we'll put that in a file called `Makefile`.
Here's how we start with that:
```
$ cd cmd
$ mkdir gethrtime
$ cd gethrtime
$ cp ../../prototypes/prototype.Makefile Makefile
$ cp ../../prototypes/prototype.c gethrtime.c
$
```
Recall from the [layout section](./layout.html) that the prototypes are versions of file that are
all set up for use. They already have the necessary copyright blobs and have
some nice sections for you to fill out. So, next we'll write the `gethrtime.c` C
file. Our program is simple, takes no arguments, and outputs the current high
resolution timestamp.
```
$ vi gethrtime.c
$ cat gethrtime.c
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*/
/*
* Copyright 2013 (c) Joyent, Inc. All rights reserved.
*/
/*
* A simple example command which outputs the current high resolution time.
*/
#include <stdio.h>
#include <sys/time.h>
int
main(void)
{
hrtime_t start;
start = gethrtime();
(void) printf("%lld\n", start);
return (0);
}
$
```
The C program itself should be straightforward. However, the `Makefile` deserves
a bit more attention. Now, let's look at the `Makefile` itself:
```
$ cat Makefile
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 Joyent, Inc. All rights reserved.
#
PROG = gethrtime
include ../Makefile.cmd
all: $(PROG)
install: all $(ROOTPROG)
clean:
include ../Makefile.targ
```
Let's step through what all of this actually means. There isn't a lot here,
that's because a good chunk of it gets taken care of by the makefiles that we
include: `Makefile.cmd` and `Makefile.targ`. Because of that we simply need to
define the `PROG` macro. If we don't specify our source files or object files,
then because of those included makefiles this automatically expands it to
include `gethrtime.c`.
There are multiple targets that we have to define. The `all` target is supposed
to build all of the software. Again, because we're leveraging this framework, we
can rely on it to do all the heavy lifting of building our program for us. The
`install` target takes care of putting it into the right place. Again, we can
leverage a lot of the macros that already exist. In this case what `ROOTPROG`
does is make sure that the program is installed into `/usr/bin` in the proto
area with its default permissions.
The `clean` target here does nothing. Recall that we have both `clean` and
`clobber` targets that are valid at different levels. The main distinction is
that `clean` only gets rid of intermediate files such as object files, where as
`clobber` does everything that `clean` does and ensures that we also remove all
of the generated programs. Because we have just one C file here, we can go
directly to and from the generated binary without intermediary files.
Now, we can go ahead and build it and test it out.
```
$ cd cmd/gethrtime
$ dmake
...
$ echo $?
0
$ ./gethrtime # The output will be different for everyone
40274477601353
$
```
Now, we need to move on to the next phase.
#### Adding it to the Build
Right now, our new `gethrtime` command exists and is sitting in the tree, but if
you ran `nightly` it wouldn't actually get built. So to fix that we need to edit
the list of subdirectories that are listed in `cmd/Makefile`. There are a few
different lists that exist here. There are different lists of subdirectories for
programs that are architecture specific and a list for common ones. For
`gethrtime` we just need to add it to the list `COMMON_SUBDIRS`. So add
`gethrtime` to that list, maintaining alphabetical order, and then we can go and
build.
```
$ cd cmd
$ vi Makefile
$ dmake gethrtime
...
$
```
And that's it. Now we've done and added a simple command. To finish up, we'd
want to go and [add a manual page](#manual-pages) and make sure that our code is
`cstyle` and `pbchk` are all clean.
#### Multi-Architecture commands
Earlier, we discussed the case where you may need to make both a 32-bit and
64-bit version of the same command. Here let's take our previous `gethrtime`
example and now make it suitable for multiple architectures. To do that we need
to do a few different things:
- Create an architecture specific directory
- Write a makefile to drive the building of each architecture
- Move common settings into a `Makefile.com`
- Rewrite our top-level `Makefile` to recur into architecture subdirectories
To do the first part we need to simply create one directory for each of the
different machine architectures that this program is going to run on. Since this
is a common command across both x86 and SPARC, we need to create four
directories:
```
$ cd usr/src/cmd/gethrtime
$ dmake clobber # Clean up from last time
$ mkdir i386 amd64 sparc sparcv9
$
```
Now that we've done this, we can go ahead and create a Makefile to drive each of
the subdirectories and write `Makefile.com` to help drive that. Again, we'll
start this process by actually copying the prototype file into place.
```
$ cd usr/src/cmd/gethrtime
$ cp ../../prototypes/prototype.Makefile Makefile.com
$ cp ../../prototypes/prototype.Makefile i386/Makefile
$ cp ../../prototypes/prototype.Makefile amd64/Makefile
$ cp ../../prototypes/prototype.Makefile sparc/Makefile
$ cp ../../prototypes/prototype.Makefile sparcv9/Makefile
```
Now that we've set ourselves up, we're going to focus on getting a single
architecture building correctly. We'll start with `i386` and also fill out
`Makefile.com`. Once this works, we can then go ahead and get the other
architectures plumbed up.
##### The Common Makefile
Our previous Makefile was really quite simple. As a part of this we need to add
a bit more complexity to our Makefile. This means that instead of directly
building the program without any intermediary object files, we're going to use
them. This is also the same technique we would have to use for multi-source
files. Let's take a look at this Makefile in a bit more detail:
```
$ cd usr/src/cmd/gethrtime
$ cat Makefile.com
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 Joyent, Inc. All rights reserved.
#
PROG = gethrtime
OBJS = gethrtime.o
FILEMODE = 0555
CLEANFILES += $(OBJS)
include ../../Makefile.cmd
CFLAGS += $(CCVERBOSE)
CFLAGS64 += $(CCVERBOSE)
all: $(PROG)
$(PROG): $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
%.o: ../%.c
$(COMPILE.c) $<
clean:
-$(RM) $(CLEANFILES)
include ../../Makefile.targ
```
To start it off, we have a familiar definition, `PROG`. Again, this is the name
of the resulting command, mainly, `gethrtime`. Next we have to declare the
`OBJS`. We use both throughout the build and to help drive
other targets. In `OBJS` we place a list of our object files. In this case we only
have one source file and thus one object file, `gethrtime.c` and `gethrtime.o`
respectively. If we were to have multiple object files, we would list them all
in that section.
The next bit we, setting `FILEMODE`, is used as part of the install target.
You'll notice that the install target isn't actually defined here. It is instead
defined in the architecture-specific section as it has slightly different things
that it needs to do based on the machine architecture. Next, we have a bit about
`CLEANFILES`. Here we're adding the list of object files to it which is in
addition to anything else that we might have had.
The `CCVERBOSE` values tell the compilers through the tool `cw` to enable
stricter checking. This roughly translates to the use of `-Wall` in `gcc`. Note
that we have both `CFLAGS` and `CFLAGS64`. In this case we have independent
values for the two different architectures.
Now we get to some of the more interesting bits. The next thing that we have
here are the actual targets. Again, like before, the `all` target will always
build our program. The next target is a bit more interesting. Here we start to
use the different pieces of the infrastructure provided to us by our inclusion
of `Makefile.cmd`. `LINK.c` is a macro which is provided by Makefile.cmd and
takes care of determining how we should invoke the compiler driver to link a
binary. Note that we still have to specify all of the `OBJS` that we want to use
and potentially any `LDLIBS`. `gethrtime` only links against `libc.so` so we do
not need additional libraries. The `POST_PROCESS` macro does a few different
things. Among them it uses mcs(1) to update the comment in the binary to
that of the current build.
The next portion here is the rule to generate the `.o` files from the `.c`
files. Here we again use another macro, `COMPILE.c` which knows several things
such as the compiler, the cflags, and the cppflags. You should never directly
invoke the compiler, only through the macros.
From here on out, things stay relatively straightforward. Previously we
didn't worry about a `clean` target, but now that we've generated
intermediate object files we have to go through and clean them up.
Finally, we end things with an include of `Makefile.targ` from inside of
the `cmd` directory.
##### The Architecture Makefiles
With this in hand, we should look at the architecture specific directory.
Compared to the last Makefile, this is actually now rather simple:
```
$ cat i386/Makefile
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 Joyent, Inc. All rights reserved.
#
include ../Makefile.com
install: all $(ROOTPROG32)
```
These two lines are powerful. The first one brings in `Makefile.com` which is
the common makefile we just finished discussing. Next we have the `install`
target. You'll note that it doesn't have any recipe, only dependencies. The
first dependency, on `all`, you'll remember from our common makefile. That takes
care of building the program. Next we have the `ROOTPROG32` macro. This macro
ensures that `PROG` gets installed as a 32-bit program under `/usr/bin`.
We can use that same exact makefile in the `sparc` directory. For `amd64` and
`sparcv9` we need a slightly different `Makefile` in those directories. That
`Makefile` looks like:
```
$ cat amd64/Makefile
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 Joyent, Inc. All rights reserved.
#
include ../Makefile.com
include ../../Makefile.cmd.64
install: all $(ROOTPROG64)
```
There are only two differences here. After we included `Makefile.com` we needed
to include `Makefile.cmd.64`. This makes sure that we end up using a different
set of definitions appropriate for 64-bit architectures. Secondly we had to
change install dependency from `ROOTPROG32` to `ROOTPROG64` as we're building
the 64-bit version of the command.
We can copy these directly into the directories for the `SPARC` architectures.
We'll copy the `i386` makefile into the `sparc` directory and the `amd64` one
into `sparcv9`.
```
$ cd cmd/gethrtime
$ cp i386/Makefile sparc/
$ cp amd64/Makefile sparcv9/
$
```
##### Driving the Build: The Makefile
The last piece we need is to set up a makefile to drive this. We need to replace
our previous version of `cmd/gethrtime/Makefile`. This will transform into
something which runs `make` recursively throughout the subdirectories. Most of
our work here is just to spawn another copy of make for our children. That's
slightly less true for `install` as there we have to handle the creation of
`/usr/bin/gethrtime` which will be a hard link to `isaexec`. Let's take
a look at this makefile.
```
$ cd cmd/gethrtime
$ cat Makefile
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 Joyent, Inc. All rights reserved.
#
PROG = gethrtime
include ../Makefile.cmd
SUBDIRS = $(MACH)
$(BUILD64)SUBDIRS += $(MACH64)
all := TARGET = all
install := TARGET = install
clean := TARGET = clean
clobber := TARGET = clobber
all clean clobber: $(SUBDIRS)
install: $(SUBDIRS)
-$(RM) $(ROOTPROG)
-$(LN) $(ISAEXEC) $(ROOTPROG)
$(SUBDIRS): FRC
@cd $@; pwd; $(MAKE) $(TARGET)
FRC:
include ../Makefile.targ
$
```
You'll see that we start of by declaring `PROG` again. You might ask why we're
doing this, but the answer lies in our `install` target where we end up needing
to create `/usr/bin/gethrtime`. The next thing we do is declare that one of the
subdirectories we need to descend into is `MACH`. This is set to be the basic
32-bit architecture of the machine that we're building. You'll see that the
addition of `MACH64` is set based on the contents of `BUILD64`. The `BUILD64`
macro will evaluate to the character `#` if we are not building the 64-bit
target and only the 32-bit target.
The next section are all conditional assignments. We use this as part of our
strategy to recurse. The general strategy for the subdirectories is that we want
to run the same target inside each of them, these assignments are how we ensure
that the `TARGET` macro is correct when we execute the `SUBDIRS` recipe. If we
follow along, we'll see that most of our targets just depend on `SUBDIRS`. In
general, this is exactly what you want.
Let's briefly pick apart the `install` target. It has a dependency on `SUBDIRS`.
This is very important, without this we will not end up descending into
architecture subdirectories and running the install. The next bit creates the
hard link from `/usr/lib/isaexec` to `/usr/bin/gethrtime`. You'll note that we
explicitly remove the old link before creating the new one. This is a common
motif in all of our makefiles and while we haven't been calling it directly,
it's been happening every time that we ran a recipe for macros such as
`ROOTPROG32`.
The next section declares the recipe for `SUBDIRS`. We have it depend on a phony
target which does not exist. This will ensure that we always run the recipe. The
recipe itself just changes into the child directory and then runs make with the
same target that the original makefile was invoked with. We follow this up with
the specific definition for FRC. Because this does not actually generate
anything, we can remain confident that it will always cause the `SUBDIRS` target
to be out of date and thus cause us to run again. To wrap everything up, we do
one last include for `Makefile.targ`.
#### Adding Multiple Source Files
Now that we're this far, adopting this setup to have multiple source files is
actually quite simple. There is only one change that we need to make to our
`Makefile.com` file at this point: the new object file to the `OBJS`
variable. If we had a new file called `foo.c` our Makefile.com looks like:
```
$ cat cmd/gethrtime/Makefile.com
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 Joyent, Inc. All rights reserved.
#
PROG = gethrtime
OBJS = gethrtime.o foo.o
FILEMODE = 0555
CLEANFILES += $(OBJS)
include ../../Makefile.cmd
CFLAGS += $(CCVERBOSE)
CFLAGS64 += $(CCVERBOSE)
all: $(PROG)
$(PROG): $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
%.o: ../%.c
$(COMPILE.c) $<
clean:
-$(RM) $(CLEANFILES)
include ../../Makefile.targ
$
```
To add any additional source files, all you have to do is append it to the list
in `OBJS`. If you need to add a file which isn't a C file, say an assembly file
or a file written using `lex` or `yacc`, then you'll need to also add a new
pattern rule in `Makefile.com` to transform that. For something like `lex` and
`yacc` you would write a rule that went from say a `.l` file to a `.c` file and
then existing rule would take you the rest of the way.
#### CTF Support
Having `CTF` data in programs is essential for debugging run away commands. For
more on `CTF`, please see ctf(5). While every kernel module and a majority of
libraries in the system have CTF data, the same is not true for commands. Adding
CTF itself, is really quite easy though. We need to once again edit
`Makefile.com`. The `cmd` makefiles already have on that adds in all the hooks
we need to add CTF support. We'll need to add a line to include the file
`Makefile.ctf` right after we include `Makefile.cmd`.
The next thing we need to do is make sure that the rules in `Makefile.com`
include the correct post processing commands. There are generic post processing
macros which `Makefile.ctf` hooks into. What you need to add is a call to
`POST_PROCESS_O` after you build object files and a call to `POST_PROCESS` after
you build executables or shared libraries. Once you make those changes, you can
run `dmake clobber` and `dmake install` again. We can verify that we in fact
have `CTF` data by checking for the `.SUNW_ctf` section with elfdump(1).
Let's put this all together:
```
$ cd cmd/gethrtime
$ vi Makefile.com
$ cat Makefile.com
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
#
# Copyright (c) 2013 Joyent, Inc. All rights reserved.
#
PROG = gethrtime
OBJS = gethrtime.o
FILEMODE = 0555
CLEANFILES += $(OBJS)
include ../../Makefile.cmd
include ../../Makefile.ctf
CFLAGS += $(CCVERBOSE)
CFLAGS64 += $(CCVERBOSE)
all: $(PROG)
$(PROG): $(OBJS)
$(LINK.c) -o $@ $(OBJS) $(LDLIBS)
$(POST_PROCESS)
%.o: ../%.c
$(COMPILE.c) $<
$(POST_PROCESS_O)
clean:
-$(RM) $(CLEANFILES)
include ../../Makefile.targ
$ dmake clobber
...
$ dmake install
...
$ elfdump -c i386/gethrtime | grep .SUNW_ctf
Section Header[31]: sh_name: .SUNW_ctf
$
```
If you do not see any output from `elfdump`, then CTF data was not properly
generated. A common reason for this happening is forgetting to clobber and
install again. You may see output but with a different number for the 'Section
Header', that is perfectly fine. Assuming that it worked, you're good to go and
now have useful CTF data for debugging.
## Libraries
Where as commands form the human interfaces to illumos, libraries make up the
programmatic interfaces to the system. You can find all of the libraries inside
of `usr/src/lib`.
Libraries generally fall into two different categories, public libraries and
private libraries. Public libraries are the common programming interfaces that
everyone expects. These libraries are what implement the standard C library
calls such as 'open(2)' and 'strlen(3C)'. There are also libraries which are
just used to implement system commands. For example, something like `dladm(8)`
uses the library `libdladm.so.1` to help implement some of its functionality.
Libraries are always built and designed to support both 32-bit and 64-bit
programs. This practice is referred to as *multilib*. 32-bit libraries for a
given architecture are found in `/usr/lib` and the 64-bit libraries are found in
`/usr/lib/64`. With one or two rare exception and unfortunate exceptions, the
libraries in illumos are all written in the C programming Language.
Unlike the commands, the libraries have a much more uniform and coherent
structure. Let's take a look at it.
### Parts of a Library
Let's take a look at the library `libproc` which is found in
`usr/src/lib/libproc`. `libproc` is the library that implements all of the
various `proc(1)` tools like `pfiles(1)` and `pstack(1)`. Let's move into that
directory and look at the top level directory.
```
$ cd usr/src/lib/libproc
$ ls -F
amd64/ common/ i386/ Makefile Makefile.com sparc/
sparcv9/
```
At this top level we can see that we have two different makefiles which control
different parts of the compilation process and then there are five directories.
The `common` directory contains all of the code, headers, and mapfiles
that are common to all architectures. For most libraries, this will
contain all of the code and headers. Next, each library should have an
architecture specific directory for each supported architecture. That's why you
see one for both 32-bit and 64-bit x86 and SPARC. The top level Makefile
recurses into all of the architecture-specific subdirectories to build the
actual library and takes care of a few targets itself related to headers and
message files.
`Makefile.com` on the other hand is a makefile that contains all of the common
aspects of building the library. This makefile is never run directly, rather
this common makefile is included by each of the architecture makefiles.
The architecture-specific directories contain makefiles that drive the actual
build process. The top-level 'Makefile' will recurse into them and invoke the
corresponding target. This is similar to multiple architecture commands. These
directories may also include code. For example, if we look at the `i386`
directory for `libproc` we'll see the following:
```
$ cd lib/libproc/i386
$ ls
Makefile Pisadep.c
$
```
In the case of libproc in addition to the Makefile we have an additional source
file that's specific to the i386 build. As part of building, the various objects
will show up in the architecture specific directories.
### Mapfiles, Public Interfaces, and Versioning
While every library is different, there is one thing which is the same across
every library: they have a mapfile. That is found as `common/mapfile-vers` so if
you consider `libproc` that means the file is located at
`usr/src/lib/libproc/common/mapfile-vers`. Mapfiles are very important and they
should be manipulated very carefully. Every mapfile refers you to read
`README.mapfiles` which can be found in `usr/src/lib`. While this guide will
cover some of the basics, you should read that file if you're manipulating any
mapfile.
We generally use mapfiles to control what symbols in shared libraries can be
used by external programs. The mapfile is used as part of the linking process.
The linker will ensure that any symbol that is not listed in the mapfile is not
visible to other programs which would like to use that library for linking.
Simply not including a prototype in a header file is not sufficient!
The mapfile also takes care of versioning the various symbols that exist in the
library. Symbols are broken down into two categories, public and private. A
private symbol means that this symbol, generally a function, is an
implementation detail of illumos. Private symbols can have their ABI and
semantics changed at any time or be removed entirely! Public symbols on the
other hand are the exact opposite. Once a public symbol is introduced, illumos
guarantees to users that this function is a stable and committed interface,
meaning that we will go out of our way to make sure that not only the API, but
the ABI are preserved. As an example, `libc` provides the traditional `open(2)`
function. That function is a public symbol and is inside of libc's mapfile.
Libraries themselves have versions. When shared libraries first came out this
was expressed by what's called the `soname` or shared object name. If we take
`libc` as an example, its soname is `libc.so.1`. The version of the soname comes
from that version suffix. When a program gets compiled and links against a
shared library, that soname is included in the program's ELF headers. When the
program is executed or a shared library is loaded, all of the mentioned
dependencies will be found and loaded by the runtime link-editor.
Versioning of the soname was notated as NAME.MAJOR.MINOR.MICRO. Whenever the
major version changed, the library was known to have broken both API and ABI
compatibility. While this worked, it wasn't the best solution. The big problem
with this versioning scheme is that it requires programs to either be rebuilt so
they can find the newer library or a series of symlinks to be updated from one
version to another.
To solve this problem the notion of a symbol version became a first class
entity. Every time a new set of symbols is introduced into illumos it is added
to a new version in the mapfile-vers file. When the shared library is built, it
notes which symbols are related to which versions. When a program is linked