GO implementation start parameter loading

Guided by classmates who have just learned Go must think about the startup process of the GO program, and how this question can be seen how the GO program runs. Today we narrow the problem to learn how the GO program loads the boot parameters, and how to perform parameter analysis.

C parameter analysis

Studying Children’s shoes in C language must be unfamiliar with Argc and Argv.

The C program always executes from the main function main, and in the main function of the parameter, naming naming of Argc and Argv is used as the main function parameters in accordance with the convention.

Among them, the argument count represents the number of line arguments, and the argument value is used to store pointers arranging to parameters.

#includeintmain (intargc, char * argv []) {printf (“argc =% d

“, argc); Printf (” Argv [0] =% S, Argv [1] =% S, Argv [2] =% s

“, argv [0], argv [1], argv [2]); Return0;}

Compilation to implement the above C code, get the output is as follows

$ GCCC_MAIN.C-OMAIN $. / mainfoobarsssddddargc = 5Argv [0] =. / main, argv [1] = foo, argv [2] = BAR

That is in the Go language, how do you get a line argument?

Os.Args loading

Like C, the GO program is also executed from the main function (user layer), but ARGC and Argv are not defined in the main function.

We can get the command line parameters through the Os.Args function.

PackageMainImport (“FMT” “OS”) funcmain () {fori, v: = rangeos.Args {fmt.printf (“arg [% d]:% V

“, i, v)}}}}

Compilation execution Go function

$ Gobuildmain.go $. / mainfoobarsssdddddarg [0]: ./maarg [1]: fooarg [2]: bararg [3]: SSSARG [4]: ??DDD

Like C, the first parameter is also a representative executable.

Load implementation

Here we need to show some Go assembly code. In order to facilitate the readers, first understand the re-abstract of the CPU through two figures.

X86 / AMD64 architecture

GO pseudo register

GO compilation In order to simplify the writing of assembly code, the four pseudo registers of PC, FP, SP, SB are introduced.

Four pseudo registers plus other universal registers are re-abstracts of the CPU in the Go assembly language. Of course, the abstract structure is also suitable for other non-x86 types of architectures.

Back to the topic, the parsing process of the command line parameter is part of the program startup.

Taking the AMD64 system as an example, the implementation of the GO program is located in Runtime / RT0_Linux_AMD64.s.

TEXT_RT0_AMD64_LINUX (SB), NOSPLIT, $ – 8JMP_RT0_AMD64 (SB)

_RT0_AMD64 function is implemented in runtime / asm_amd64.s

Text_RT0_AMD64 (SB), NOSPLIT, $ – 8MOVQ0 (SP), DI // Argcleaq8 (SP), Si // ArgvjmPruntime ¡¤ RT0_GO (SB)

Did you see Argc and Argv? Here, they are loaded from the stack memory to Di, Si registers, respectively.

The RT0_GO function completes all the initialization work of Runtime, but we only follow the process of the ARGC and Argv.

TEXTruntime ¡¤ rt0_go (SB), NOSPLIT | TOPFRAME, $ 0 // copyargumentsforwardonanevenstackMOVQDI, AX // argcMOVQSI, BX // argvSUBQ $ (4 * 8 + 7), SP // 2args2autoANDQ $ ~ 15, SPMOVQAX, 16 (SP) MOVQBX, 24 (SP) … MOVL16 (SP), AX // Copyargcmovlax, 0 (SP) MOVQ24 (SP), AX // Copyargvmovqax, 8 (SP) CallRuntime ¡¤ args (sb) CallRuntime ¡¤ Osinit (SB) CallRuntime ¡¤ Schedinit (SB) … After a series of operations, Argc and Argv are also tossing back to the stack memory 0 (SP) and 8 (SP).

The Args function is located in Runtime / Runtime1.go

VAR (argcint32argv ** Byte) Funcargs (CINT32, V ** BYTE) {argc = cargv = vsysargs (c, v)}

Here, Argc and Argv are saved to the variable runtime.argc and runtime.argv.

After calling the ARGS function in the RT0_GO function, SCHEDINIT will be executed.

Funcschedinit () {… goARGS () …

GoARGS is implemented in runtime / runtime1.go

Varargslice [] StringFuncGoargs () {ifgoos == “windows” {return} argslice = make ([] string, argc) fori: = int32 (0); i

The purpose of this function is to include the command line parameter string pointer to the stack memory, and the String type of Go is encapsulated, and finally saved in Runtime.Argslice.

There is a knowledge point here, how is the Go encapsulates the C string into a Go string type? The answer is in the following code.

funcgostringnocopy (str * byte) string {ss: = stringStruct {str: unsafe.Pointer (str), len: findnull (str)} s: = * (* string) (unsafe.Pointer (& ss)) returns} funcargv_index (argv ** Byte, IINT32) * BYTE {Return * (** Byte) (add (unsafe.pointer (argv), uintptr (i) * sys.ptrsize)} Funcadd (Punsafe.Pointer, xuintptr) unsafe.pointer {returnusafe .Pointer (uintptr (p) + x)}

At this point, the GO has saved the information of Argc and Argv to Runtime.Argslice, and the smart you must guess the os.Args method is to read the SLICE.

In OS / PROC.GO, it is its implementation

Varargs [] stringfuncinit () {ifruntime.goos == “windows” {// initializedinexec_windows.go.return} args = runtime_args ()} funcruntime_args () [] string // Inpackageruntime

The implementation of the Runtime_args method is an os_Runtime_args function in Runtime / Runtime.go

//go:linknameos_runtime_argsos.runtime_argsfuncos_runtime_args () [] string {returnappend ([] string {}, argslice …)}

The copy of Runtime.argslice is achieved here. At this point, the Os.Args method finally successfully loaded the command line parameter Argv information.

Summarize

This article we introduced the Go’s command line parameters when the Os.Args parsing program was started, and learned its implementation process.In the source code to load implementation, we found that if you go back from a point, you will trace its implementation principle, this process is not complicated, I hope that children’s shoes should not be afraid to study the source code.

Os.Args method stores command line parameters in a string slice, and can extract them through traversal.But in actual development, we generally do not directly use the Os.Args method, because GO provides us with a better FLAG package.But in view of the reason, the part is written later.