1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "elf_versioning.h"
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11
12#include "images.h"
13
14
15static status_t
16assert_defined_image_version(image_t* dependentImage, image_t* image,
17	const elf_version_info& neededVersion, bool weak)
18{
19	// If the image doesn't have version definitions, we print a warning and
20	// succeed. Weird, but that's how glibc does it. Not unlikely we'll fail
21	// later when resolving versioned symbols.
22	if (image->version_definitions == NULL) {
23		FATAL("%s: No version information available (required by %s)\n",
24			image->path, dependentImage->path);
25		return B_OK;
26	}
27
28	// iterate through the defined versions to find the given one
29	elf_verdef* definition = image->version_definitions;
30	for (uint32 i = 0; i < image->num_version_definitions; i++) {
31		uint32 versionIndex = VER_NDX(definition->vd_ndx);
32		elf_version_info& info = image->versions[versionIndex];
33
34		if (neededVersion.hash == info.hash
35			&& strcmp(neededVersion.name, info.name) == 0) {
36			return B_OK;
37		}
38
39		definition = (elf_verdef*)((uint8*)definition + definition->vd_next);
40	}
41
42	// version not found -- fail, if not weak
43	if (!weak) {
44		FATAL("%s: version \"%s\" not found (required by %s)\n", image->path,
45			neededVersion.name, dependentImage->name);
46		return B_MISSING_SYMBOL;
47	}
48
49	return B_OK;
50}
51
52
53// #pragma mark -
54
55
56status_t
57init_image_version_infos(image_t* image)
58{
59	// First find out how many version infos we need -- i.e. get the greatest
60	// version index from the defined and needed versions (they use the same
61	// index namespace).
62	uint32 maxIndex = 0;
63
64	if (image->version_definitions != NULL) {
65		elf_verdef* definition = image->version_definitions;
66		for (uint32 i = 0; i < image->num_version_definitions; i++) {
67			if (definition->vd_version != 1) {
68				FATAL("%s: Unsupported version definition revision: %u\n",
69					image->path, definition->vd_version);
70				return B_BAD_VALUE;
71			}
72
73			uint32 versionIndex = VER_NDX(definition->vd_ndx);
74			if (versionIndex > maxIndex)
75				maxIndex = versionIndex;
76
77			definition = (elf_verdef*)
78				((uint8*)definition + definition->vd_next);
79		}
80	}
81
82	if (image->needed_versions != NULL) {
83		elf_verneed* needed = image->needed_versions;
84		for (uint32 i = 0; i < image->num_needed_versions; i++) {
85			if (needed->vn_version != 1) {
86				FATAL("%s: Unsupported version needed revision: %u\n",
87					image->path, needed->vn_version);
88				return B_BAD_VALUE;
89			}
90
91			elf_vernaux* vernaux
92				= (elf_vernaux*)((uint8*)needed + needed->vn_aux);
93			for (uint32 k = 0; k < needed->vn_cnt; k++) {
94				uint32 versionIndex = VER_NDX(vernaux->vna_other);
95				if (versionIndex > maxIndex)
96					maxIndex = versionIndex;
97
98				vernaux = (elf_vernaux*)((uint8*)vernaux + vernaux->vna_next);
99			}
100
101			needed = (elf_verneed*)((uint8*)needed + needed->vn_next);
102		}
103	}
104
105	if (maxIndex == 0)
106		return B_OK;
107
108	// allocate the version infos
109	size_t size = sizeof(elf_version_info) * (maxIndex + 1);
110	image->versions
111		= (elf_version_info*)malloc(size);
112	if (image->versions == NULL) {
113		FATAL("Memory shortage in init_image_version_infos()");
114		return B_NO_MEMORY;
115	}
116	memset(image->versions, 0, size);
117	image->num_versions = maxIndex + 1;
118
119	// init the version infos
120
121	// version definitions
122	if (image->version_definitions != NULL) {
123		elf_verdef* definition = image->version_definitions;
124		for (uint32 i = 0; i < image->num_version_definitions; i++) {
125			if (definition->vd_cnt > 0
126				&& (definition->vd_flags & VER_FLG_BASE) == 0) {
127				elf_verdaux* verdaux
128					= (elf_verdaux*)((uint8*)definition + definition->vd_aux);
129
130				uint32 versionIndex = VER_NDX(definition->vd_ndx);
131				elf_version_info& info = image->versions[versionIndex];
132				info.hash = definition->vd_hash;
133				info.name = STRING(image, verdaux->vda_name);
134				info.file_name = NULL;
135			}
136
137			definition = (elf_verdef*)
138				((uint8*)definition + definition->vd_next);
139		}
140	}
141
142	// needed versions
143	if (image->needed_versions != NULL) {
144		elf_verneed* needed = image->needed_versions;
145		for (uint32 i = 0; i < image->num_needed_versions; i++) {
146			const char* fileName = STRING(image, needed->vn_file);
147
148			elf_vernaux* vernaux
149				= (elf_vernaux*)((uint8*)needed + needed->vn_aux);
150			for (uint32 k = 0; k < needed->vn_cnt; k++) {
151				uint32 versionIndex = VER_NDX(vernaux->vna_other);
152				elf_version_info& info = image->versions[versionIndex];
153				info.hash = vernaux->vna_hash;
154				info.name = STRING(image, vernaux->vna_name);
155				info.file_name = fileName;
156
157				vernaux = (elf_vernaux*)((uint8*)vernaux + vernaux->vna_next);
158			}
159
160			needed = (elf_verneed*)((uint8*)needed + needed->vn_next);
161		}
162	}
163
164	return B_OK;
165}
166
167
168status_t
169check_needed_image_versions(image_t* image)
170{
171	if (image->needed_versions == NULL)
172		return B_OK;
173
174	elf_verneed* needed = image->needed_versions;
175	for (uint32 i = 0; i < image->num_needed_versions; i++) {
176		const char* fileName = STRING(image, needed->vn_file);
177		image_t* dependency = find_loaded_image_by_name(fileName,
178			ALL_IMAGE_TYPES);
179		if (dependency == NULL) {
180			// This can't really happen, unless the object file is broken, since
181			// the file should also appear in DT_NEEDED.
182			FATAL("%s: Version dependency \"%s\" not found", image->path,
183				fileName);
184			return B_ENTRY_NOT_FOUND;
185		}
186
187		elf_vernaux* vernaux
188			= (elf_vernaux*)((uint8*)needed + needed->vn_aux);
189		for (uint32 k = 0; k < needed->vn_cnt; k++) {
190			uint32 versionIndex = VER_NDX(vernaux->vna_other);
191			elf_version_info& info = image->versions[versionIndex];
192
193			status_t error = assert_defined_image_version(image, dependency,
194				info, (vernaux->vna_flags & VER_FLG_WEAK) != 0);
195			if (error != B_OK)
196				return error;
197
198			vernaux = (elf_vernaux*)((uint8*)vernaux + vernaux->vna_next);
199		}
200
201		needed = (elf_verneed*)((uint8*)needed + needed->vn_next);
202	}
203
204	return B_OK;
205}
206