Dynamic connection and loading

xiaoxiao2021-03-06  44

Dynamic connection and loading

$ Revision: 2.3 $

$ Date: 1999/06/15 03:30:36 $

Dynamic connection makes most of the connection process delay until the program starts running. This approach provides the advantages that many other methods are difficult to achieve:

● Dynamic connection libraries are easier to create than static connection libraries.

● Dynamic Library is easier to update than static connection libraries.

● The semantics of the dynamic connection library and the non-shared library are very close. (What is the semantic index here)

● Dynamic connection library allows programs to load and uninstall routines at runtime, which is a function of making it difficult to provide.

Of course, it also has some shortcomings. Relative to those static connections, performance cost of dynamic connection is much, because most of the connecting processes must be done every time the program is running. All dynamic connection symbols in the program have to find and resolve through a symbol table (Symbol Talbles). (Windows DLLS slightly reduces these consumption, we will explain below) Since the dynamic library must contain these symbolic tables, they are more than static libraries.

In addition to the compatibility problem (???), a Chronic Source of Problems IS Changes in Library Semantics. Dynamic connection libraries are easy to update relative to static connection libraries and non-shared libraries, so the library of the program is compared Easy, this means that there is a possibility that the state of these programs has changed in the case of unknowing. This is a common problem under Microsoft's Windows platform, and the Windows platform uses a large number of shared libraries, which have a lot of versions, and lack of relatively complete version control. Most programs come with the libraries they need, and the installer often covers the new version of the old version inadvertently, making programs that need new version of the shared library feature cannot run. Although some friendly installations will pop up a warning before the new version of the new version of the library, however, if the new version of the library replaces the old version of the library, relying on the older repository will not run.

ELF (Executable and Linkable Format) dynamic connection

Sunos' s Sunos introduced a dynamic connection library into UNIX at the end of the 1980s. Subsequently, the Unix System V Release 4 assisted by Sun assisted in the United States, and allows it to adapt to the Sun's system. Compared to past objective file formats, ELF is undoubtedly a progress. In the late 1990s, ELF became the standard of UNIX and class UNIX operating systems (such as Linux and BSD derived systems).

ELF file content

As mentioned in Chapter 3, an ELF file is from the perspective of the Linker, a collection of sections; from the perspective of the program loader (Loader), it is some segment (segments) set. The program and shared libraries in the ELF format have the same structure, just some of the collection of segments and the collection of segments.

The shared library in the ELF format can be loaded to any address, so the shared library uses the PIC (where the location is independent code), so that the text page (how to translate it) does not need to be relocated, and can be shared by multiple processes. As mentioned in Chapter 8, the connector in the ELF format supports PIC code via GOT (Global Offset Table). The GOT contains all of the dynamic libraries all referenced static data pointers, and the dynamic connector is parsed and relocated all pointers through GOT. These may result in some performance issues, but in fact, in addition to some large libraries, GOT is usually small; generally a standard C library only 350K code and 180 GOT portals. Figure 10-1: Plt and got

Picture of Program with plt

Picture of Library with plt and got

GOT and calling it is in the same ELF file, so regardless of the program loaded to a place, the relative position of the GOT will not change. All code can locate the GOT with relative addresses, put the GOT address to the register, then load GOT The content, it points to a static data. If a library does not quote static data, you may not include GOT, but in fact, all the libraries contain a GOT.

To support dynamic connections, each ELF sharing library and each executable of each use shared library has a PLT (Procedure Linkage Table). Similar to the GOT access data, the PLT provides an indirect access function. PLT supports lazy mode (Lazy Evaluation), until the function is first called before the function is called. Considering that the PLT item is much more than the GOT item (nearly 600 in a universal C library), most of the routines will not be called at all, so using the inert mode can save the start time and accelerate the entire program.

Details of the PLT will be discussed below:

A dynamically connected ELF file contains all connection information (the original text is the connector information, according to the original statement, it means that it means to cohere, I don't know what your understanding is, this information is running Help the connector to restocate the file and parse all symbols. Among them, the Dynamic Symbol Table, contains all of the import and export symbols; .dynStr section and .hash section contain the name of the symbol, the hash table can be used to accelerate the symbol lookup.

Dynamically connected ELF files have a special place, Dynamic segment (or called. Dynamic section), dynamic connector uses it to find some necessary information. It is loaded as a data segment, but the ELF file includes a pointer to it to ensure that the running dynamic connector can find it. .dyNamic section is a list of pointers and labels. Some items are only now programs, some items only appear in the library, and some are in both.

● Needed: The necessary library text name (generally in the program, when a library relies on another library, you can also appear in the library, the number of times can be more than once)

● Soname (Shared Object Name): The file name to be used by the connector. (in stock)

● SymTab, Strtab, Hash, Syment, StRSZ: points to symbol tables, associated string tables, hash table, symbol table size, string table size. (Program and library)

● Pltgot: Points to GOT, pointing to PLT on some platforms. (Program and library) ● REL, RELSZ, RELENT (may also be called RELA, RELASZ, RELAENT): Points to the number and size of the reset entry, redirection the entrance. The difference between REL and RELA is that RELA has an additive. (Program and library)

● JMPrel, Pltrelsz, And Pltrel: Poin, Size, And Format

(REL or RELA) of Relocation Table for Data Referred to by the Plt.

(Both.) (How do you understand it in the middle of Format?)

● INIT and FINI: The initialization routine and end cleanup routine that point to the program startup and end (optional, but usually the program and the library)

● In addition to the top, there are some uncommon items.

A complete ELF shared library is shown below. The first is the read-only part, including the symbol table, PLT, code, read-only data, and then readable and write parts, including general data, got, .dynamic section, the last read-only section is generally followed by BSS segment, but usually it Do not appear in the file

Figure 10-2: An Elf Shared Library

(LOTS of Pointer Arrows Here)

Read-Only Pages:

.hash

.dynsym

.dynstr

.plt

.Text

. rodata

Read-Write Pages:

.DATA

.got.

. Dynamic

.bss

Load a dynamic connection program

A dynamically connected ELF program is a long but direct process.

Start the dynamic connector

When the operating system runs the program, IT Maps in The File's Pages As Normal (here should refer to the mirror image of the hard disk to memory, but the File's page here is not good), pay attention to the executable Interpreter section, the explanation it refers to Dynamic Connector: ld.so, ld.so itself is a shared library of ELF format. When operating the system startup program, first map the dynamic connector to a suitable address and then pass the auxiliary vector (auxiliary) information required to connect a connector to the stack, and finally launch LD.SO.

Auxiliary vector (auxiliary information) includes:

● AT_phdr, at_phent and at_phnum: The address of the program file header, the size and number of entry items in the program header. The entry item describes the segment information of the file. If the system has not mapped the program to memory, there will be an AT_EXECFD entry entry, which contains the file description of the program file.

● AT_ENTRY: Program start address, the dynamic connector will jump to this place after initialization.

● AT_BASE: The address of the dynamic connector is loaded.

At this point, the self-boot code started by the LD.SO first looks for its own GOT, the first entry entry to the LD.SO file points to the Dynamic segment in the ld.so file. With the Dynamic segment, the connector can find your own redirect port, redevel the pointer in your own data segment, parse the code location of the basic routine loaded with other symbols. (Linux ld.so is named _dt_ to these basic routines, use a specific code to look for symbols starting with _dt_, then parse them)

The connector is then initialized to initialize the symbol table chain with program symbol table pointer and connector symbol table. Conceptually, the program and loaded all libraries share a symbol table, merged with all symbolic tables, which is not asked by the connector to maintain a linked list for each file symbol table. Each file contains a hash table, through some hash, the hash chain table corresponding to each head, the connector can quickly find the symbol of the symbol, quickly find the desired symbol, accelerate the symbol Query THEN Running THROUGH APRIATE (this word seems to spell incorrectly) Hash chain in each of the symbol table in the list. Library

Once the connector completes its initialization, look up the name of the library required by the program. There is a pointer in the program head to the Dynamic segment, which contains dynamic connection information, and DT_STRTAB in the Dynamic segment points to a character table, where the DT_Needed contains an offset relative to the character table, pointing to the desired library name. (Note: Assuming the use of the array stratab [n] [] of the character table to describe, then the DT_Needed field is a number greater than 0, less than n, set to X, then the library name specified by DT_Needed is stratab [x])

For each library, the connector first looks for library file locations, essentially a fairly complex process. DT_NEEDED The library text name is generally similar to libxt.so.6 (XT development kit, version 6), library files may also have a rename file in any library file directory. In my system, the actual file name of this library is /usr/x11r6/lib/libxt.so.6.0, the last ".0" represents the secondary version number.

The connector finds the following places:

● First, see if the .dyNamic segment contains an item called DT_RPath (it is a list of colonally separated library file search directories). This item is when the program is connected by the connector (not the dynamic connector LD.SO, but the so-called static connector LD), is added by the command line switch or environment variable. It is often applied to subsystems, such as database applications, we want to load some program collections and support libraries to a directory.

● Check if there is an environment variable ld_library_path (it is a list of colonally separated library files). This item can help developers build a new version of library, add his path to LD_Library_path, use it with existing connectors to test new libraries, or Equally Well to Instrument the Behavior of the program (IT Skips this Step if the program

IS set-uid, for security reasons.

● The connector views the library cache file /etc/ld.so.conf, which contains a corresponding list of library names and paths. If the library name exists, the connector uses its corresponding path, and use this lookup method to find most The library (the file name does not need and requires full compliance, this can refer to the next "library version").

● If the lookup of the above failed, the connector finds the default path / usr / lib, if the library file is still not found, then an error is displayed.

After the connector finds the library file, open it, then read the ELF header, find the pointer to each segment (to Find the program header which in turn) to the file's segments include the Dynamic Segment, there should be a better translation the way). The connector is assigned a space for the code segment and data segment of the library and maps to memory, followed by BSS (no space). . Database's. Dynamic segment, the connector adds this library to the symbolic table to the symbolic chain. If the library is dependent on other libraries, the library is added to the loading queue. After completing this process, all the libraries have been mapped, and Loader has logically has a global symbolic table, which is a combination of all programs and the symbolic table of mapping libraries.

Initialization of shared libraries

Now Loader once again accesses each library, the redirect entry of the process library, fill the GOT of the library, and relocate the data segment of the library. The load type of load under x86 includes:

● R_386_glob_dat, used to initialize the GOT item, the symbol address is defined in another library.

● R_386_32, non-GOT item, symbolic address definition in another library. (What is the difference above?)

● R_386_RELATIVE, redirect data, is a typical is a string pointer or a partially defined static data.

● R_386_JMP_slot, used to initialize the GOT item of the PLT, and will be detailed later.

If a library has the .init section, it is called a specific initialization routine that is library, such as a C static constructor; also, the program is running .fini routine when exiting. (The work is not completed by the main program main, but the program self-start code, in Linux, these code in _Glibc_start_main in ld.so). When the above work is completed, all the libraries are fully loaded and ready to run, the Loader final call (at_entry), the program starts.

Using the inert connection process of the PLT

Programs using dynamic libraries typically contain a lot of function calls, but many of them will not be called at a certain run, which may be an error handling routine or a function that does not use this program. In addition, each shared library also includes calls for other library functions, but it is even less than in the run of a program, because the program is not directly or indirect calls.

For the start of the acceleration program, the dynamically connected ELF program uses an inert mode to bind the address. That is, a function (or variable) determines its address when it is first called.

ELF supports inert binding through PLT (Procedure Linkage Table), each dynamically connected program and shared library have a PLT, program, or shared library for each non-local routine (function ??), in the PLT contains one Entrance item, Figure 3. It should be noted that the PIC code is used in the PC, which is also a PIC code, so it can be part of a read-only code section.

Figure 10-3: Plt Structure in x86 code

Special First Entries

PLT0: Pushl Got 4

JMP * got 8

Regular Entries, Non-Pic Code: PCN: JMP * GOT M

Push #reloc_offset

JMP PLT0

Regular Entries, Pic Code:

PLTN: JMP * Got M (% EBX)

Push #reloc_offset

JMP PLT0

Access to a routine in a program or shared library is adjusted to become access to the PLT entry. The program or shared library visits a routine for the first time, and the PLT entry calls the dynamic connector to resolve the actual address of this routine. Since then, PLT

The entry directly jumps to the actual address, so after the first call is completed, the consumption of the PLT is only an additional jump instruction, and there is no extra consumption when returned.

The first entrance in the PLT called PLT0, which is a special code to access the dynamic connector. When loading, the dynamic connector automatically sets two values ​​in the GOT. In Got 4 (which is got [1]), it put some specific library identity. In GOT 8 (ie got [2]), it places the address of the dynamic connector's symbol parsing routine. (A large number of routine and function, two words, strict translations should be "routines" and "procedures", so that it is not convenient, in general, I think there is no need to strict Distinguish, generally translated into "procedures", do not know if it is?)

The remaining entrance to the PLT is called PLTN, each item starts with a direct jump (see figure10-3, regular entries). Each PLT entry item corresponds to a GOT entry entry, the GOT item is initialized to a pointer, pointing to the address of the PUSH instruction that the PLT directly jumps the instruction. (In the PIC file, the relocation needs to be required, but the symbol query is costly), and the jump instruction is behind is a PUSH instruction, press a offset value (#reloc_offset) to press the stack, the offset value specifies the file. In the relocation table, a repositioning entry entry for R_386_JMP_SLOT, the symbol reference of the reset entry item (R_OFFSET, Refer to the following) Point a symbol of the symbol table, its symbol address (R_INFO, refer to the following note) Point one GOT entrance.

In the Linux, there is a table of repositioning information, usually called REL.PLT, which contains a relative address and other information of a symbol, and its data structure is

Typedef struct {

ELF32_ADDR R_OFFSET;

ELF32_WORD R_INFO;

ELF32_REL;

R_offset points to the corresponding GOT entry, R_INFO points to a symbol of the symbol table. In this way, we pass the #reloc_offset, which is the address of the reset entry entry. After pressing it into the stack, the latter program can find the corresponding symbol information, resolve the symbol, and then store the specified GOT)

This compact but comparative arrangement means: When the first program or shared library accesses the PLT entry item, because the pointer inside the GOT refers to the PLT item, the first jump in the entrance item of the PLT does not have any Usage. The PUSH instruction presses a offset value into the stack. This offset value indicates the symbol you want to resolve, and the symbol resolution to which GOT item, then jump to the PLT0, the code inside the plt0 first put the program or library The identifier is pressed into the stack, then jumps to the symbol of the dynamic connector parsing the code section, so that two values ​​are pressed in the top of the stack. (Note, using jump rather than calling, the above two values ​​of the press is the return address of the PLT code) Now (stub code), save all registers, call a dynamic connector to resolve the symbol . The two identifiers in the stack are sufficient to find the library symbol table and the entrance to the symbol table. Dynamic Connector uses a symbolic table chain to find a symbol value, then store the address of the routine in the specified GOT item. The Stub Code is then restored, and two values ​​in which PLT are pressed, exit this code. The GOT item has been updated at this time, in the call, the PLT jumps directly to the routine without the need to dynamically connect.

Dynamically connected other special points

In order to maintain the semantics of the runtime, there are many hidden code processing, and the ELF connector and dynamic connector have many special circumstances.

Static initialization

If a program references a global variable defined in the shared library, the connector must create a copy of a variable for it because the data address of the program must be determined, the connector must create a copy of a variable, such as Figure4. Since the dynamic connector can correct these addresses through a GOT pointer, there is generally no problem. However, if the shared library initializes this variable, the program will not be known. To solve this problem, the connector sets a type R_386_COPY in the relocation table of the program (otherwise contains only R_386_JMP_SLOT, R_386_386_GLOB_386_RELATIVE), pointing to the address of the variable in the program, then tells the dynamic connector from The shared library will copy the initialization value of the variable.

Figure 10-4: Global Data Initialization

Main program:

EXTERN INT TOKEN;

Routine in Shared Library:

Int token = 42;

Although this feature is very basic in some types of code, it is extremely very small in practice. Since only acts on a single data, it is a meter. But initialized is usually a function or data pointer, so this compromise is sufficient.

Library version

The name of the dynamic library has a primary version number and the secondary number, such as libc.so.1.1 but the program only recognizes the main version number, like libc.so.1. The secondary version is just to support compatibility.

In order to make the program quickly load, the system maintains a library buffer file, which contains the full path name of each library. When a new library is installed, the configuration program is responsible for updating.

In order to support this design, each dynamic connection library has a "real name" called Soname, assigned when the library is created. For example: a library called libc.so.1.1 its "real name" called libc.so.1 (Soname usually defaults to library name). When the connector creates a shared library program, it lists the Soname of the library, not a full name. The library buffer creation program scan all the paths containing the shared library, finds this shared library, finds Soname, see if there are multiple versions, select the highest version, then write Soname and the highest version of the full path to the cache file. This will ensure that the dynamic connector can quickly find the current version of each library at runtime. Running dynamic connection

Although the ELF dynamic connector often implicitly invoke the PLT entry while the program is running, the program can also explicitly use DLOPEN () to load a shared library, with DLSYM () to find a symbol (usually a function) address. These two routines are simply packaged in a dynamic connector into a callback function. When the dynamic connector loads a library through DLOPEN (), it is the same relocation and symbol parsing like it on other libraries. Therefore, all dynamic loaders can load and reference global data when running without any special callback functions. (So ​​the Dynamically Loaded Program Call Back to Routines Already loaded and refer to global data in the running program. In the end, I can translate better, my translation is some questions :)

These features allow users to modify the original code of the program, you can add additional functions to the program, or even do not stop and restart (this program is useful in the database and web server). As early as early as early as early as early as the early 1960s, the operating system of the mainframe provides a similar function, although there is no connection to the exit like this, but it has brought quite large to the package for packaging for a long time. Elasticity. It provides programs to expand their own way, write a new program with C or C , run the compiler and connectors to create a shared library, then dynamically load and run new code. (Mainframe Sort Program Have Linked and Loaded Custom Inner loop code for each sort job for decades.)

Microsoft Dynamic Connection Library

Microsoft Windows provides shared libraries called dynamic connection libraries or DLLs. They are very similar to the ELF sharing library, but it is just simple. Windows3.1's 16-bit dynamic connection library and 32-bit dynamic connection libraries under Windows NT / 95 have essentially changed. Here we only discuss the new Win32 library. The import process of the DLLS is similar to the PLT. Although it is designed to use a method similar to the GOT method to import data, it is actually used a simpler scheme to use explicit code (To Dereference Imported Pointers, Dereference If you use this meaning, it is a bit uncommon) to share data.

In Windows, all programs and DLLs are PEable Executable files that are mapped as virtual memory to process space. Unlike all applications in Windows3.1, Win32 assigns an address space for each application and maps the executor and library to the address space used. For read-only code segments, these two methods have no difference, but for data in DLLs, the system will assign a copy of the data for the program. (Some of this approach is too simple, although the PE file can also specify a section to share the data segment, share a single data to all programs that use the library, but in most cases, it is not like this), dynamically connect Although it is part of the kernel (in Linux, the dynamic connector is an application), but it is loaded with executable programs and DLLs, which is similar to the ELF program loaded with a dynamic connection. First, the kernel maps the executable information through the section information in the PE header, and then uses the PE header information of the DLL file to map the dynamic connection library.

The PE file can also contain relocation information. However, an executable can generally do not contain relocation information, and its mapping address has been determined when the connection is connected. All DLLs contain a relocation entry, and if the DLLS is not available when the mapped address determined, the relocation item will be used to relocate yourself. (Microsoft is said to be REBASING at runtime

There is an entry point in two PE files with executables and DLLs. When the DLLS is loaded or unloaded, and each binding and uninstalling the DLLS each time the DLL, the loader calls the DLLS entry point (the loader will pass a parameter to explain the reason). This approach is similar to the .init and .fini festival in the ELF, make the programmer to do something before the program is initialized and exited. (This Provides a hook for static initializers and destructors analog to the elf .init and .fini sections. The sentence above is completely used)

Input and output symbols in the PE file

PE supports shared libraries through two special segments in the file. Edata (used to output symbols) list the output symbol of a file; .idata (enter symbol) List the input symbol of a file. Program files are usually only .idata segments, while DLLs always have .EDATA segments, if DLLS uses other DLLs, there may be .idata. A symbol can be exported by symbolic name or order (indicating a small integer of symbols in the symbolic address table). The sequence lookup symbol can improve the connection efficiency, however, the developer is difficult to ensure that the order of each version is consistent, and the possibility of making mistakes is large. So in practice, only the order is used when changing very little system services, and other situations use the symbolic name.

The .edata section contains an export directory table that describes other sections, which follows the symbol export table.

Figure 10-5: .edata section structure

Export Directory Pointing TO:

Export Address Table Export Address Table

Ordinal Table Document

Name Pointer Table Name Pointer

Name strings name string

The export address table contains the RVA (Relative Virtual Address, relative virtual addresses, with respect to the offset of the PE file load address). If rva refers to back. The aedata section, it is a "Forwarder" reference, and the value pointed to is a string naming the symbol to use to satisfy the reference, probably de-fix in a different dll. (How do this sentence understand? The order table and the name pointer table are parallel, each item of the name pointer table is the RVA of the symbolic name. The item value of the order table is the number of the symbol at the address export table (the number of orders does not need to start with 0, and the number of orders will be subtracted) The reference value (this value is usually 1) is the number of the export symbol table). Export symbols do not necessarily need a name. However, they usually have names. The symbols in the name pointer table are arranged in alphabetical order so that the loader uses two bibarry findings. The purpose of the .idata section is in contrast, and they map symbols or orders back into a virtual address. The array of several 0 ends of the .IDATA section, including the import directory table, the import lookup table for each DLL, and finally the name list.

Figure 10-6: Structure of .IDATA section

Import Directory Tables, with LOTSA Arrows

Each Has Import Lookup Table RVA, TIME / DATE Stamp, Forwarder Chain (Unused?), DLL Name, Import Address RVA TABLE

NULL

Import Table, Entries with High Bit Flag (Table Per DLL)

Hint / name Table

There is an array maintenance import DLL address in the code segment of the program to resolve this address when loading. The import lookup table is marked the symbol to import, and its entry item and the item import address table are associated. Each lookup entry consists of 32 bits. If the highest bit is 1, the lower 31 bit is a sign of a symbol, otherwise the lower 31 bit is the RVA address in the Hint / Name Table. Each Hint / Name entry has 4 bytes of Hint, guess symbols exported the name pointer table in DLL, followed by the symbol name ending with NULL. The program loader finds the export symbol table via Hint, if the symbol name matches, use this symbol, no two points for the entire export symbol table. (If the DLL has not changed, or at least exporting the symbol table has not changed, as long as the DLL is connected, you can find a symbol using Hint to guess.

Unlike the ELF import symbol, only the symbolic address imported by .idata is placed in the import symbol table, and anywhere in the import file will not be corrected. For the code address, slightly different, when the connector creates a executive or DLLS, to create a "Thunks" table in the code segment, indirectly jump to the import symbol table, use "Thunks" as import The address of the routine is transparent to the programmer. (The Thunks As Well As Most of The Data In The .idata Section Actually Come From A Stub Library Created At The Same Time As The DLL.) In the latest version of the C / C compiler in Microsoft, if the programmer knows to call the DLL One routine, you can declare this routine as "DLLIMPORT", the compiler will generate an indirect jump instruction to the address table to avoid additional indirect jumps. For data address, so be some problems, since it's harder to hide the extra level of indirection required to address a symbol in another executable.Traditionally, programmers just bit the bullet and explicitly declared imported variables to be pointers to the real values ​​and explicitly dereferencd (I am not enough to understand this paragraph) In the latest version of the Microsoft C and C compiler, the programmer declares that the global variable is "dllimport", the compiler will use additional pointer references, and the ELF code passed The GOT pointer to reference the data very similar. Inert binding

The new version of the Windows compiler adds the delay load function, allowing the process inert binding symbol, a bit like ELF PLT, delayed DLL and .IData export directory table have similar structures, but because it is not. IDATA, So the loader does not process it automatically. Each item of the export directory is initialized to the same routine pointer, this routine is used to find and load the DLLS, replacing the content of each item is the actual address. The delay loaded directory table has the original content of the stored introduction table, so that even if the DLL can be restored, it can restore it. Microsoft provides a standard routine, but its interface is open, if there is a need to implement one version itself.

Windows allows programs to use GetProcAddress to find symbolics using getProcAddress using LoadLibrary and FreeLibrary Explicit loading and uninstall DLLs.

DLLS and threads

If the DLL uses TLS (Thread Local Storage-threaded local storage), it will not be good in this situation. A Windows program can enable multiple threads in a process, and they share the address space of the process. Each thread has a TLS to store thread private data, such as threading resources and pointers of the data structure, and the like. TLS needs to prepare Slot for executables and DLLs that use TLS (see tabets). Windows connectors can create a .tls section in the PE executable, define the data storage order in the TLS required for the executable and any TLS required to be directly referenced. Whenever the process creates a thread, the new thread creates its own TLS as the .tls section as the touchpad.

Each TLS is a memory area that is split into several basic storage units, each of which is a slot. The number of Slot is usually thousands, and each slot can store a data. The problem is that most of the DLLs can be connected to both the actuators or through LoadLibrary explicit loading. It is unforeseen that a DLLS will be explicitly invited, so the DLLS cannot rely on the .tls section, the explicit-connected DLLS will not automatically get TLS.

Fortunately, Windows defines the API assigned Slots to TLS. Write a DLL is best to use these APIs, not .tls, unless you know that DLLS is implicit call.

OSF / 1 quasi-sharing library

OSF / 1 is from a UNIX variant with OSF (Open Software Foundation), which uses a shared library scheme between dynamic connection and static connection. Its developer said that because the static connection only needs little relocation, it is faster than the dynamic connection; at the same time, because the update of the library is not frequent, the system administrator is necessary to focus when updating the library. Connect all the programs in the system, they are willing to endure.

So OSF / 1 uses a method of global symbol table visible to all processes, all shared libraries will be loaded into a shared address space when the system is started. This means that all libraries have allocated the address, and there will be no changes in the system's runtime. Whenever a program starts, if it uses a shared library, it maps shared libraries and symbols and parsing unfined references through the global symbol table. Since the program is a connection existing address space, the relocation is completed at startup, so do not consume loading time when running.

When a shared library changes, the system has to restart, then load new libraries and create a new global symbol table.

This method is very clever, but it is not very satisfactory. First, the program lookup symbol time is longer longer than the weight, so the saving time is not necessarily possible to improve performance. Second, OSF / 1 is not capable of providing the function of running time loading and running libraries.

Let the shared library accelerate

Sharing libraries, special city ELF shared libraries may be very slow. The reason is from many aspects, there are several mentioned in Chapter 8:

● Loading time

● Load time symbol analysis

● PIC's Prolog code overhead

● Indirect data reference over PIC

● Reserved address register of PIC

The first two problems can be improved by caching, and the latter two can be improved by degrading pure PIC code.

There is a large number of address space on a modern computer, select an address range for a shared library, which is feasible to the process of using the shared library. A more efficient method taken by Windows is that whether the library is connected or the first load is loaded, temporarily bind it to an address. Then, when the library is connected each time, use the same address as much as possible, which means that no relocation is required. If this address is not available in a process, the library is relocated as before.

The SGI system uses the term QuickStart to describe the pre-transfer positioning of the process in connection, (or in a self pass over the shared library). BEOS caching it when the library is loaded in the process. Even if multiple libraries are dependent on each other, it is theoretically in multiple libraries and predicts symbols, although I didn't find such a connector.

If a system uses a pre-positioned library, PIC is not so important. All processes are loaded to pre-reigning addresses, whether the library is not a PIC, and its code can be shared. Therefore, the non-PIC library in a suitable address can actually be shared and will not lose performance like PIC. These static connection libraries based on Chapter 9, ie the address conflict, the dynamic connector moves the library to other addresses, lost some performance, but also the connection failed, Windows use this way. BEOS will relocate the library, while maintaining the correct semantics when the library changes. When BeOS found a new version of the library installed, it creates a new version of the cache when the programmer quoted this library. The change in the library has a chain reaction. If the library A library B symbols, library B is updated, library A will also create a new cache. These make the programmer's work simple. But I don't understand is, (libraries are in practice update).).).).).

Comparison of several dynamic connection methods

Dynamic connection of UNIX / ELF and Windows / PE is different in several respects.

ELF uses a name space for each program, and PE has a name space for each library. An ELF program knows the symbols and libraries it needs, but there is no corresponding relationship between symbols and libraries. In another aspect, PE knows that a symbol comes from a library, which makes PE lack flexible, but there is a greater resistance to inadvertent deception. Imagine an executable call to call a routine AFUNC and BFUNC, AFUNC in Library A, BFUNC in Library B, if a new version of the A library defines BFUNC, the ELF program gives the new BFUNC, while PE Will not this. When there is a lot of libraries, it is a trouble for ELF; a local solution method is to tell the dynamic connector from which library is from which library is from which library is searching for these libraries, then search executable Body and other libraries. The DT_SYMBOLIC field tells the dynamic connector to look for the symbolic table of the library itself, so other libraries will not override internal symbol references. (These are not always needed, consider the Malloc hack described in front of the previous chapter) These special methods can try to avoid coverage between the symbols of the library, but it is still lacking like Java, which is seen in Chapter 11. Alternatives of the hierarchical namespace.

Maintain the semantics of the static connection program, EFL is more difficult than PE. In an LEF program, the reference to the data in the external library is automatically parsed, and PE is specially processed as imported data. PE is in a comparison of a pointer and function, because an address of an import function is "Thunk" instead of the actual address in the library. The ELF file treats all the pointers.

At runtime, almost all Windows dynamic connectors are in the kernel, while the dynamic connector of the EFL is part of the application, the kernel is only mapped to the initialization file. Windows methods are obviously fast because it does not have to map and relocate dynamic connector each time you start connecting in the process. The definition of ELF is more flexible. Because each executive uses a program called "Interpreter" to do connectors (now general connector called LD.so), different executables can use different translation programs without the need to change. This actually makes it easy to support a variety of versions of Unix, such as Linux and BSD, can support non-local executables by manufacturing a compatible dynamic connector.

Exercise

In the ELF shared library, the function calls in the library are generally implemented through the PLT, and the function address is bound when running, is this useful? why? Imagine a program calling the routine plugh () in a shared library, and then the programmer creates a dynamic connection program using the library, and later the system administrator notes that Plugh () is a stupid name, installed a new version Library, this routine is called XSAZQ in this library. What will happen when the next programmer runs this program.

If a runtime environment variable ld_bind_now is set, the EFL dynamic connector is bound to all PLT portions of the program when loading. In the previous question, if ld_bind_now is set, what will be?

Microsoft relysces some additional techniques in the connector, and some mechanisms in operating system existing mechanisms, the inert binding is implemented without kernel. So provide access to transparent sharing data, avoiding multiple pointers in the current plan, how difficult?

engineering

Creating a complete dynamic connector is unrealistic, because most of the work is dynamically connected during runtime, not when it is connected. At 8-3 We create a PIC executive body that has already made a lot of work for creating a shared library. A dynamically connected shared library is a PIC executter and a defined import and export symbol list, and the library it rely on. Create such a file into a shared library or an executable that uses a shared library:

Linklib Lib1 LIB2 ...

LINK LIB1 LIB2 ...

Where lib1 lib2 is the library that is dependent on this dynamic connection

Project 10-1: Extend the functionality of the Project 8-3 version of the connector to process the shared library and the executive that needs to share libraries. The connector combines a series of files and shared libraries to merge them into an executive or library. The output file contains a symbolic table that contains defined symbols (export) and undefined symbols (import). Relocation Types Are The Ones for Pic Files Along With as4 and rs4 for References To Imported Symbols.

Project 10-2: Write a runtime Binder to parse all its references in an executive that uses a dynamic connection library. It should first read the executive, then read the necessary libraries, relocate them to the appropriate non-overlapping address, then create a logical symbol table (you can also actually create this table, using a chain list or such as ELF To do it), finally parse all internal and external references. After these, all code and data should be placed to memory space, and their address should be parsed and relocated to the specified address.

转载请注明原文地址:https://www.9cbs.com/read-58152.html

New Post(0)