Skip to contentSkip to conversation

__vdso_findsym

RFC In review Andy Lutomirski 6 participants 39 messages Jun 14, 2014 – Jun 20, 2014

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover __vdso_findsym

The idea is to add AT_VDSO_FINDSYM pointing at __vdso_findsym. This implements __vdso_findsym.

This would make it easier for runtimes that don't otherwise implement ELF loaders to use the vdso.

Thoughts?

If people like the basic concept, I'll finish it, write up documentation and a selftest. If people don't like it, I'll drop it.

Andy Lutomirski (2):
uapi: Add some missing dynamic table-related definitions to elf.h [NOT READY] x86/vdso: Add __vdso_findsym

 arch/x86/vdso/Makefile          |   3 +
 arch/x86/vdso/vdso-findsym.c    | 136 ++++++++++++++++++++++++++++++++++++++++
 arch/x86/vdso/vdso-layout.lds.S |  11 ++--
 arch/x86/vdso/vdso.lds.S        |   1 +
 arch/x86/vdso/vdso2c.h          |   2 +-
 include/uapi/linux/elf.h        | 121 +++++++++++++++++++++++++++++++++++
 6 files changed, 268 insertions(+), 6 deletions(-)
 create mode 100644 arch/x86/vdso/vdso-findsym.c
Signature · 1 line
1.9.3
#1 lore raw

RFC 1/2 uapi: Add some missing dynamic table-related definitions to elf.h

This adds STN_UNDEF and ELF symbol version data structures.

---
 include/uapi/linux/elf.h | 121 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 121 insertions(+)
include/uapi/linux/elf.h +121 -0
index ef6103b..8b7cf23 100644
@@ -134,6 +134,12 @@ typedef __s64	Elf64_Sxword;
 #define ELF64_ST_BIND(x)	ELF_ST_BIND(x)
 #define ELF64_ST_TYPE(x)	ELF_ST_TYPE(x)
 
+/* Symbol table indices are found in the hash buckets and chain table
+   of a symbol hash table section.  This special index value indicates
+   the end of a chain, meaning no further symbols are found in that bucket.  */
+
+#define STN_UNDEF	0		/* End of a chain.  */
+
 typedef struct dynamic{
   Elf32_Sword d_tag;
   union{
@@ -197,6 +203,121 @@ typedef struct elf64_sym {
   Elf64_Xword st_size;		/* Associated symbol size */
 } Elf64_Sym;
 
+/* Version definition sections.  */
+
+typedef struct
+{
+  Elf32_Half	vd_version;		/* Version revision */
+  Elf32_Half	vd_flags;		/* Version information */
+  Elf32_Half	vd_ndx;			/* Version Index */
+  Elf32_Half	vd_cnt;			/* Number of associated aux entries */
+  Elf32_Word	vd_hash;		/* Version name hash value */
+  Elf32_Word	vd_aux;			/* Offset in bytes to verdaux array */
+  Elf32_Word	vd_next;		/* Offset in bytes to next verdef
+					   entry */
+} Elf32_Verdef;
+
+typedef struct
+{
+  Elf64_Half	vd_version;		/* Version revision */
+  Elf64_Half	vd_flags;		/* Version information */
+  Elf64_Half	vd_ndx;			/* Version Index */
+  Elf64_Half	vd_cnt;			/* Number of associated aux entries */
+  Elf64_Word	vd_hash;		/* Version name hash value */
+  Elf64_Word	vd_aux;			/* Offset in bytes to verdaux array */
+  Elf64_Word	vd_next;		/* Offset in bytes to next verdef
+					   entry */
+} Elf64_Verdef;
+
+
+/* Legal values for vd_version (version revision).  */
+#define VER_DEF_NONE	0		/* No version */
+#define VER_DEF_CURRENT	1		/* Current version */
+#define VER_DEF_NUM	2		/* Given version number */
+
+/* Legal values for vd_flags (version information flags).  */
+#define VER_FLG_BASE	0x1		/* Version definition of file itself */
+#define VER_FLG_WEAK	0x2		/* Weak version identifier */
+
+/* Versym symbol index values.  */
+#define	VER_NDX_LOCAL		0	/* Symbol is local.  */
+#define	VER_NDX_GLOBAL		1	/* Symbol is global.  */
+#define	VER_NDX_LORESERVE	0xff00	/* Beginning of reserved entries.  */
+#define	VER_NDX_ELIMINATE	0xff01	/* Symbol is to be eliminated.  */
+
+/* Auxialiary version information.  */
+
+typedef struct
+{
+  Elf32_Word	vda_name;		/* Version or dependency names */
+  Elf32_Word	vda_next;		/* Offset in bytes to next verdaux
+					   entry */
+} Elf32_Verdaux;
+
+typedef struct
+{
+  Elf64_Word	vda_name;		/* Version or dependency names */
+  Elf64_Word	vda_next;		/* Offset in bytes to next verdaux
+					   entry */
+} Elf64_Verdaux;
+
+
+/* Version dependency section.  */
+
+typedef struct
+{
+  Elf32_Half	vn_version;		/* Version of structure */
+  Elf32_Half	vn_cnt;			/* Number of associated aux entries */
+  Elf32_Word	vn_file;		/* Offset of filename for this
+					   dependency */
+  Elf32_Word	vn_aux;			/* Offset in bytes to vernaux array */
+  Elf32_Word	vn_next;		/* Offset in bytes to next verneed
+					   entry */
+} Elf32_Verneed;
+
+typedef struct
+{
+  Elf64_Half	vn_version;		/* Version of structure */
+  Elf64_Half	vn_cnt;			/* Number of associated aux entries */
+  Elf64_Word	vn_file;		/* Offset of filename for this
+					   dependency */
+  Elf64_Word	vn_aux;			/* Offset in bytes to vernaux array */
+  Elf64_Word	vn_next;		/* Offset in bytes to next verneed
+					   entry */
+} Elf64_Verneed;
+
+
+/* Legal values for vn_version (version revision).  */
+#define VER_NEED_NONE	 0		/* No version */
+#define VER_NEED_CURRENT 1		/* Current version */
+#define VER_NEED_NUM	 2		/* Given version number */
+
+/* Auxiliary needed version information.  */
+
+typedef struct
+{
+  Elf32_Word	vna_hash;		/* Hash value of dependency name */
+  Elf32_Half	vna_flags;		/* Dependency specific information */
+  Elf32_Half	vna_other;		/* Unused */
+  Elf32_Word	vna_name;		/* Dependency name string offset */
+  Elf32_Word	vna_next;		/* Offset in bytes to next vernaux
+					   entry */
+} Elf32_Vernaux;
+
+typedef struct
+{
+  Elf64_Word	vna_hash;		/* Hash value of dependency name */
+  Elf64_Half	vna_flags;		/* Dependency specific information */
+  Elf64_Half	vna_other;		/* Unused */
+  Elf64_Word	vna_name;		/* Dependency name string offset */
+  Elf64_Word	vna_next;		/* Offset in bytes to next vernaux
+					   entry */
+} Elf64_Vernaux;
+
+
+/* Legal values for vna_flags.  */
+#define VER_FLG_WEAK	0x2		/* Weak version identifier */
+
 
 #define EI_NIDENT	16
 
Signature · 1 line
1.9.3
#2 lore raw

RFC 2/2 x86/vdso: Add __vdso_findsym

In reply to Andy Lutomirski

The intent is to add a painless way for userspace libraries to find vdso symbols. This will work by adding a vdso function __vdso_findsym and a new auxvec entry AT_VDSO_FINDSYM that points at it.

This isn't done yet. __vdso_findsym works, but it's only available for the 64-bit vdso, and AT_VDSO_FINDSYM isn't implemented.

Getting this to work for x32 will require hackery or an actual x32 toolchain to build it.

---
 arch/x86/vdso/Makefile          |   3 +
 arch/x86/vdso/vdso-findsym.c    | 136 ++++++++++++++++++++++++++++++++++++++++
 arch/x86/vdso/vdso-layout.lds.S |  11 ++--
 arch/x86/vdso/vdso.lds.S        |   1 +
 arch/x86/vdso/vdso2c.h          |   2 +-
 5 files changed, 147 insertions(+), 6 deletions(-)
 create mode 100644 arch/x86/vdso/vdso-findsym.c
arch/x86/vdso/Makefile +3 -0
index ba6fc27..62e6938 100644
@@ -16,7 +16,9 @@ vdso-install-$(VDSO32-y)	+= $(vdso32-images)
 
 # files to link into the vdso
 vobjs-y := vdso-note.o vclock_gettime.o vgetcpu.o vdso-fakesections.o
+vobjs-y += vdso-findsym.o
 vobjs-nox32 := vdso-fakesections.o
+vobjs-nox32 += vdso-findsym.o
 
 # files to link into kernel
 obj-y				+= vma.o
@@ -83,6 +85,7 @@ CFLAGS_REMOVE_vdso-note.o = -pg
 CFLAGS_REMOVE_vclock_gettime.o = -pg
 CFLAGS_REMOVE_vgetcpu.o = -pg
 CFLAGS_REMOVE_vvar.o = -pg
+CFLAGS_REMOVE_vdso-findsym.o = -pg
 
 #
 # X32 processes use x32 vDSO to access 64bit kernel data.
arch/x86/vdso/vdso-findsym.c new file +136 -0

136 lines not on this page · raw message

arch/x86/vdso/vdso-layout.lds.S +6 -5
index 2ec72f6..2e8b692 100644
@@ -8,14 +8,15 @@
 
 SECTIONS
 {
+	VDSO_START = .;
 	. = SIZEOF_HEADERS;
 
-	.hash		: { *(.hash) }			:text
+	.hash		: { DYN_HASH = .; *(.hash) }		:text
 	.gnu.hash	: { *(.gnu.hash) }
-	.dynsym		: { *(.dynsym) }
-	.dynstr		: { *(.dynstr) }
-	.gnu.version	: { *(.gnu.version) }
-	.gnu.version_d	: { *(.gnu.version_d) }
+	.dynsym		: { DYN_SYMTAB = .; *(.dynsym) }
+	.dynstr		: { DYN_STRTAB = .; *(.dynstr) }
+	.gnu.version	: { DYN_VERSYM = .; *(.gnu.version) }
+	.gnu.version_d	: { DYN_VERDEF = .; *(.gnu.version_d) }
 	.gnu.version_r	: { *(.gnu.version_r) }
 
 	.note		: { *(.note.*) }		:text	:note
arch/x86/vdso/vdso.lds.S +1 -0
index 75e3404..9b23d4e 100644
@@ -22,6 +22,7 @@ VERSION {
 		__vdso_getcpu;
 		time;
 		__vdso_time;
+		__vdso_findsym;
 	local: *;
 	};
 }
arch/x86/vdso/vdso2c.h +1 -1
index c6eefaf..931abac 100644
@@ -51,7 +51,7 @@ static void GOFUNC(void *addr, size_t len, FILE *outfile, const char *name)
 	for (i = 0; dyn + i < dyn_end &&
 		     GET_LE(&dyn[i].d_tag) != DT_NULL; i++) {
 		typeof(dyn[i].d_tag) tag = GET_LE(&dyn[i].d_tag);
-		if (tag == DT_REL || tag == DT_RELSZ ||
+		if (tag == DT_REL || tag == DT_RELA || tag == DT_RELSZ ||
 		    tag == DT_RELENT || tag == DT_TEXTREL)
 			fail("vdso image contains dynamic relocations\n");
 	}
Signature · 1 line
1.9.3
#3 lore raw
In reply to Andy Lutomirski
Quoted 10 lines · Andy Lutomirski

The idea is to add AT_VDSO_FINDSYM pointing at __vdso_findsym. This implements __vdso_findsym.

This would make it easier for runtimes that don't otherwise implement ELF loaders to use the vdso.

Thoughts?

If people like the basic concept, I'll finish it, write up documentation and a selftest. If people don't like it, I'll drop it.

I like the idea. I don't think it's immediately useful, since libraries wanting to use vdso will likely want to support older kernels that don't yet have this, and thus need to include their own vdso parsing code. However, if we could get this in now, it would be very useful a few years down the line where optimizing for old kernels is not an important goal. I'd certainly like to be able to drop the ELF parsing code from musl to reduce static binary size. And I could see other projects possibly using it just to avoid the issue of ever adding ELF parsing code in the first place.

Rich

#4 lore raw
Quoted 12 lines · Rich Felker
Quoted 10 lines · Andy Lutomirski

The idea is to add AT_VDSO_FINDSYM pointing at __vdso_findsym. This implements __vdso_findsym.

This would make it easier for runtimes that don't otherwise implement ELF loaders to use the vdso.

Thoughts?

If people like the basic concept, I'll finish it, write up documentation and a selftest. If people don't like it, I'll drop it.

Quoted 10 lines

I like the idea. I don't think it's immediately useful, since libraries wanting to use vdso will likely want to support older kernels that don't yet have this, and thus need to include their own vdso parsing code. However, if we could get this in now, it would be very useful a few years down the line where optimizing for old kernels is not an important goal. I'd certainly like to be able to drop the ELF parsing code from musl to reduce static binary size. And I could see other projects possibly using it just to avoid the issue of ever adding ELF parsing code in the first place.

Hmm. I hadn't thought of this as a size win, but I guess it could be if there are static binaries around.

This patch currently generates 488 bytes on text with my toolchain. More than half of that is garbage needed to deal with the symbol version tables, and I should be able to move a decent fraction of it to vdso2c and therefore out of the kernel and vdso images.

--Andy

#5 lore raw
Quoted 4 lines · Andy Lutomirski

Hmm. I hadn't thought of this as a size win, but I guess it could be if there are static binaries around.

That pretty much *is* the win, because this is in effect treating the kernel vdso as a shared library.

Quoted 4 lines

This patch currently generates 488 bytes on text with my toolchain. More than half of that is garbage needed to deal with the symbol version tables, and I should be able to move a decent fraction of it to vdso2c and therefore out of the kernel and vdso images.

Now, if we can do this without adding another page then that becomes much more compelling IMO. I suspect we can if you are already down to 488 bytes.

-hpa

#6 lore raw
Quoted 5 lines · H. Peter Anvin

Now, if we can do this without adding another page then that becomes much more compelling IMO. I suspect we can if you are already down to 488 bytes.

(If it isn't already the case, was inteded to be part of that...)

-hpa

#7 lore raw
Quoted 8 lines · H. Peter Anvin
Quoted 5 lines · H. Peter Anvin

Now, if we can do this without adding another page then that becomes much more compelling IMO. I suspect we can if you are already down to 488 bytes.

(If it isn't already the case, was inteded to be part of that...)

It's down to 375 bytes now. I'm not sure how to do better than that without starting to hardcode variously nasty assumptions.

With CONFIG_PARAVIRT_CLOCK=y I think the 64-bit vdso is two pages anyway. With CONFIG_PARAVIRT_CLOCK=n, it's one page even with my code.

(At some point I plan on tackling the paravirt clock code. It's much more complicated and much slower than it needs to be.)

--Andy

#8 lore raw
In reply to Andy Lutomirski
Quoted 7 lines · Andy Lutomirski

The idea is to add AT_VDSO_FINDSYM pointing at __vdso_findsym. This implements __vdso_findsym.

This would make it easier for runtimes that don't otherwise implement ELF loaders to use the vdso.

Thoughts?

I'm opposed to this based on the principle that the kernel should NOT be a dumping ground for random code that user-space can and should implement for itself. As long as the vdso is correctly formatted ELF, then parsing it is easy, and the kernel should not care at all if or how user-space accesses it.

The fact that golang got it wrong is not an argument for moving this functionality into the kernel.

/Mikael

#9 lore raw
Quoted 7 lines · Mikael Pettersson
Quoted 6 lines · Andy Lutomirski

The idea is to add AT_VDSO_FINDSYM pointing at __vdso_findsym. This implements __vdso_findsym.

This would make it easier for runtimes that don't otherwise implement ELF loaders to use the vdso.

Quoted 7 lines
Quoted 1 line

Thoughts?

I'm opposed to this based on the principle that the kernel should NOT be a dumping ground for random code that user-space can and should implement for itself. As long as the vdso is correctly formatted ELF, then parsing it is easy, and the kernel should not care at all if or how user-space accesses it.

Arguably, it was a mistake for the kernel to expose a virtual ELF to begin with, and it should just have exposed a "lookup function by name" operation to begin with. Yes this can be done in userspace, but I see it more as a matter of "fixing a broken API design".

Rich

#10 lore raw
Quoted 16 lines · Rich Felker
Quoted 7 lines · Mikael Pettersson
Quoted 6 lines · Andy Lutomirski

The idea is to add AT_VDSO_FINDSYM pointing at __vdso_findsym. This implements __vdso_findsym.

This would make it easier for runtimes that don't otherwise implement ELF loaders to use the vdso.

Quoted 7 lines
Quoted 1 line

Thoughts?

I'm opposed to this based on the principle that the kernel should NOT be a dumping ground for random code that user-space can and should implement for itself. As long as the vdso is correctly formatted ELF, then parsing it is easy, and the kernel should not care at all if or how user-space accesses it.

Quoted 4 lines

Arguably, it was a mistake for the kernel to expose a virtual ELF to begin with, and it should just have exposed a "lookup function by name" operation to begin with. Yes this can be done in userspace, but I see it more as a matter of "fixing a broken API design".

Is the __vdso_findsym(name, version) approach good? I don't know why the vdso uses symbol versions at all, nor do I know why it uses weak symbol aliases. The vdso's "clock_gettime" isn't a drop-in replacement for glibc's: it doesn't touch error, and it returns negative error codes.

It might make more sense to promise that the vdso will never export to functions with the same name but different versions and to just get rid of the version field from __vdso_findsym. I tried implementing this; it's untested, but the text size is only 230 bytes :)

It might also make sense to look up by number. Yes, it smells like Windows DLLs, but it also looks awfully like Linux syscalls, which do just fine using a call-by-number scheme.

--Andy

#11 lore raw
In reply to Rich Felker
Quoted 6 lines · Rich Felker

Arguably, it was a mistake for the kernel to expose a virtual ELF to begin with, and it should just have exposed a "lookup function by name" operation to begin with. Yes this can be done in userspace, but I see it more as a matter of "fixing a broken API design".

What the fsck are you smoking? There is immense value in providing a stable and very well-defined data structure, which also happens to be what dynamic linkers already want to consume. Providing a helper for crippled libc applications has potential value. Shaving a few hundred bytes off static applications is a very weak argument, simply because it is such a small fraction of the enormous cost of a static application, and static applications are problematic in a number of other ways, especially the lack of ability to fix bugs.

Treating the kernel as an ersatz dynamic library for "static" applications is kind of silly -- after all, why not provide an entire libc in the vdso? I have actually seen people advocate for doing that.

-hpa

#12 lore raw
Quoted 17 lines · H. Peter Anvin
Quoted 6 lines · Rich Felker

Arguably, it was a mistake for the kernel to expose a virtual ELF to begin with, and it should just have exposed a "lookup function by name" operation to begin with. Yes this can be done in userspace, but I see it more as a matter of "fixing a broken API design".

What the fsck are you smoking? There is immense value in providing a stable and very well-defined data structure, which also happens to be what dynamic linkers already want to consume. Providing a helper for crippled libc applications has potential value. Shaving a few hundred bytes off static applications is a very weak argument, simply because it is such a small fraction of the enormous cost of a static application, and static applications are problematic in a number of other ways, especially the lack of ability to fix bugs.

Quoted 3 lines

Treating the kernel as an ersatz dynamic library for "static" applications is kind of silly -- after all, why not provide an entire libc in the vdso? I have actually seen people advocate for doing that.

To be clear, I have no desire whatsoever to give the vdso an actual ELF parser or anything else that userspace should be providing itself. I think that a special-purpose vdso parser in the vdso makes some sense, though, since userspace might otherwise provide one for the sole purpose of parsing the vdso.

And there's plenty of reasons that having the vdso be an ELF image is useful. For one thing, gdb can take advantage of it. For another, CRIU is parsing it for a rather different reason, and something like __vdso_findsym won't fill that need.

Also, given the general lack of a comprehensible specification of what the GNU flavor of the ELF format actually is [1], there's something to be said for reducing the proliferation of ELF parsers. glibc and binutils are quite unlikely to become incompatible with each other, but I sincerely doubt that anyone from binutils land is likely to review (and maintain!) my ELF parser, Go's, or a hypothetical future ELF parser from any of the other glibc-less things. If those things use one that's in the kernel, then it's easy for the kernel to guarantee that each vdso image can successfully parse itself.

[1] The only comprehensible description of the GNU hash extension that I could find is on Oracle's blog (!)

--Andy

#13 lore raw
Quoted 22 lines · Andy Lutomirski

To be clear, I have no desire whatsoever to give the vdso an actual ELF parser or anything else that userspace should be providing itself. I think that a special-purpose vdso parser in the vdso makes some sense, though, since userspace might otherwise provide one for the sole purpose of parsing the vdso.

And there's plenty of reasons that having the vdso be an ELF image is useful. For one thing, gdb can take advantage of it. For another, CRIU is parsing it for a rather different reason, and something like __vdso_findsym won't fill that need.

Also, given the general lack of a comprehensible specification of what the GNU flavor of the ELF format actually is [1], there's something to be said for reducing the proliferation of ELF parsers. glibc and binutils are quite unlikely to become incompatible with each other, but I sincerely doubt that anyone from binutils land is likely to review (and maintain!) my ELF parser, Go's, or a hypothetical future ELF parser from any of the other glibc-less things. If those things use one that's in the kernel, then it's easy for the kernel to guarantee that each vdso image can successfully parse itself.

Quoted 3 lines

[1] The only comprehensible description of the GNU hash extension that I could find is on Oracle's blog (!)

Yes, but that is why we provide the standard SysV hash. The GNU hash is not too bad, but you're absolutely right the documentation stinks.

Providing a simple symbol lookup is an opportunistic thing, and might be useful that way, and only because (as you say) the version in the vdso would only need to be guaranteed to parse a single data structure -- that same vdso.

On the other hand, it better work, correctly, in every version of the kernel, so I believe it will need to be done such that it is either correct by construction or gets self-tested during the build process so it errors out on failure. One simple way to do correct by construction would be to do the "vdso entry point by index" -- a new kind of system call numbers, in effect, as much as it has shades of Windows DLL with their "ordinal numbers".

-hpa

#14 lore raw
cc Andi Kleen, who designed the vdso
Quoted 35 lines · H. Peter Anvin
Quoted 22 lines · Andy Lutomirski

To be clear, I have no desire whatsoever to give the vdso an actual ELF parser or anything else that userspace should be providing itself. I think that a special-purpose vdso parser in the vdso makes some sense, though, since userspace might otherwise provide one for the sole purpose of parsing the vdso.

And there's plenty of reasons that having the vdso be an ELF image is useful. For one thing, gdb can take advantage of it. For another, CRIU is parsing it for a rather different reason, and something like __vdso_findsym won't fill that need.

Also, given the general lack of a comprehensible specification of what the GNU flavor of the ELF format actually is [1], there's something to be said for reducing the proliferation of ELF parsers. glibc and binutils are quite unlikely to become incompatible with each other, but I sincerely doubt that anyone from binutils land is likely to review (and maintain!) my ELF parser, Go's, or a hypothetical future ELF parser from any of the other glibc-less things. If those things use one that's in the kernel, then it's easy for the kernel to guarantee that each vdso image can successfully parse itself.

Quoted 3 lines

[1] The only comprehensible description of the GNU hash extension that I could find is on Oracle's blog (!)

Yes, but that is why we provide the standard SysV hash. The GNU hash is not too bad, but you're absolutely right the documentation stinks.

Providing a simple symbol lookup is an opportunistic thing, and might be useful that way, and only because (as you say) the version in the vdso would only need to be guaranteed to parse a single data structure -- that same vdso.

Quoted 4 lines

On the other hand, it better work, correctly, in every version of the kernel, so I believe it will need to be done such that it is either correct by construction or gets self-tested during the build process so it errors out on failure.

I was thinking of adding something to selftests that would check that __vdso_findsym can find every exported symbol, check that it can't find the ones it shouldn't find, and call it on a bunch of garbage strings to make sure it rejects them.

Quoted 4 lines

One simple way to do correct by construction
would be to do the "vdso entry point by index" -- a new kind of system call numbers, in effect, as much as it has shades of Windows DLL with their "ordinal numbers".

It's certainly easy. It's a little gross, and I sort of feel bad about having two parallel ways of referring to a vdso function -- one used by ELF parsers and one used by the new thing. Using an array also wins on speed and code size. *sigh* -- I'm torn on this one.

Do you know why the vdso uses symbol versioning and weak symbols in the first place? This seems to date back all the way to the beginning (2aae950b21e4bc789d1fc6668faf67e8748300b7). If we're going to add a new way to find vdso symbols, I would like to at least drop support for versions.

--Andy

#15 lore raw

Symbol versioning so we can rev the ABI and still provide backwards compatibility. Weak symbols so the libc can override symbols if it considers it appropriate. This is a good thing.

Quoted 76 lines · Andy Lutomirski
cc Andi Kleen, who designed the vdso
Quoted 4 lines · H. Peter Anvin
Quoted 3 lines · Andy Lutomirski

To be clear, I have no desire whatsoever to give the vdso an actual ELF parser or anything else that userspace should be providing

itself.

Quoted 5 lines
Quoted 5 lines

I think that a special-purpose vdso parser in the vdso makes some sense, though, since userspace might otherwise provide one for the sole purpose of parsing the vdso.

And there's plenty of reasons that having the vdso be an ELF image

is

Quoted 5 lines
Quoted 5 lines

useful. For one thing, gdb can take advantage of it. For another, CRIU is parsing it for a rather different reason, and something like __vdso_findsym won't fill that need.

Also, given the general lack of a comprehensible specification of

what

Quoted 1 line
Quoted 1 line

the GNU flavor of the ELF format actually is [1], there's something

to

Quoted 9 lines
Quoted 9 lines

be said for reducing the proliferation of ELF parsers. glibc and binutils are quite unlikely to become incompatible with each other, but I sincerely doubt that anyone from binutils land is likely to review (and maintain!) my ELF parser, Go's, or a hypothetical future ELF parser from any of the other glibc-less things. If those things use one that's in the kernel, then it's easy for the kernel to guarantee that each vdso image can successfully parse itself.

[1] The only comprehensible description of the GNU hash extension

that

Quoted 4 lines
Quoted 2 lines

I could find is on Oracle's blog (!)

Yes, but that is why we provide the standard SysV hash. The GNU hash

is

Quoted 3 lines

not too bad, but you're absolutely right the documentation stinks.

Providing a simple symbol lookup is an opportunistic thing, and might

be

Quoted 1 line

useful that way, and only because (as you say) the version in the

vdso

Quoted 6 lines

would only need to be guaranteed to parse a single data structure -- that same vdso.

On the other hand, it better work, correctly, in every version of the kernel, so I believe it will need to be done such that it is either correct by construction or gets self-tested during the build process

so

Quoted 1 line

it errors out on failure.

I was thinking of adding something to selftests that would check that __vdso_findsym can find every exported symbol, check that it can't find the ones it shouldn't find, and call it on a bunch of garbage strings to make sure it rejects them.

Quoted 2 lines

One simple way to do correct by construction
would be to do the "vdso entry point by index" -- a new kind of

system

Quoted 2 lines

call numbers, in effect, as much as it has shades of Windows DLL with their "ordinal numbers".

It's certainly easy. It's a little gross, and I sort of feel bad about having two parallel ways of referring to a vdso function -- one used by ELF parsers and one used by the new thing. Using an array also wins on speed and code size. *sigh* -- I'm torn on this one.

Do you know why the vdso uses symbol versioning and weak symbols in the first place? This seems to date back all the way to the beginning (2aae950b21e4bc789d1fc6668faf67e8748300b7). If we're going to add a new way to find vdso symbols, I would like to at least drop support for versions.

--Andy

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#16 lore raw
Quoted 1 line · H. Peter Anvin

Symbol versioning so we can rev the ABI and still provide backwards compatibility. Weak symbols so the libc can override symbols if it considers it appropriate. This is a good thing.

Are we ever going to change, say, the __vdso_clock_gettime ABI without renaming the function? If we want to preserve that ability, I can keep support for versions, but it seems odd.

I don't buy the weak symbol argument at all. We currently expose a strong symbol __vdso_clock_gettime and a weak alias clock_gettime. I agree that, if glibc treats us as a real DSO, then clock_gettime can't be strong, but I don't see why it should exist at all (other than for backwards compatibility).

--Andy

#17 lore raw

If it doesn't, then you incur an additional indirection penalty. The strong __vdso symbol allows the libc wrapper to fall back to the vdso implementation, the weak symbol allows three to be no wrapper at all. This is good.

The reason for changing ABI would be shifting types. This is very much how glibc manages transitions.

Quoted 16 lines · Andy Lutomirski
Quoted 1 line · H. Peter Anvin

Symbol versioning so we can rev the ABI and still provide backwards

compatibility. Weak symbols so the libc can override symbols if it considers it appropriate. This is a good thing.

Are we ever going to change, say, the __vdso_clock_gettime ABI without renaming the function? If we want to preserve that ability, I can keep support for versions, but it seems odd.

I don't buy the weak symbol argument at all. We currently expose a strong symbol __vdso_clock_gettime and a weak alias clock_gettime. I agree that, if glibc treats us as a real DSO, then clock_gettime can't be strong, but I don't see why it should exist at all (other than for backwards compatibility).

--Andy

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#18 lore raw
Quoted 4 lines · H. Peter Anvin

If it doesn't, then you incur an additional indirection penalty. The strong __vdso symbol allows the libc wrapper to fall back to the vdso implementation, the weak symbol allows three to be no wrapper at all. This is good.

The reason for changing ABI would be shifting types. This is very much how glibc manages transitions.

The purpose of symbol versioning is so that symbols with well known names, like stat, can continue to use those same names while changing types. Both old and new programs can continue to use the name stat and continue to work even though they use different types.

I don't see how this applies to the kernel VDSO. Those symbols do not use well-known names; they use names like __vdso_time. If you change the types used by those symbols, you can change the name as well. What is the downside?

Ian

#19 lore raw

The weak symbols are well-known names. The __vdso symbols are strong.

Quoted 21 lines · Ian Lance Taylor
Quoted 2 lines · H. Peter Anvin

If it doesn't, then you incur an additional indirection penalty. The

strong __vdso symbol allows the libc wrapper to fall back to the vdso implementation, the weak symbol allows three to be no wrapper at all. This is good.

Quoted 2 lines

The reason for changing ABI would be shifting types. This is very

much how glibc manages transitions.

The purpose of symbol versioning is so that symbols with well known names, like stat, can continue to use those same names while changing types. Both old and new programs can continue to use the name stat and continue to work even though they use different types.

I don't see how this applies to the kernel VDSO. Those symbols do not use well-known names; they use names like __vdso_time. If you change the types used by those symbols, you can change the name as well. What is the downside?

Ian

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#20 lore raw
Quoted 1 line · H. Peter Anvin

The weak symbols are well-known names. The __vdso symbols are strong.

I see. But I don't understand how this is supposed to work. When I link a program against gettimeofday, I get a reference to gettimeofday with version GLIBC_2.2.5. After all, I only link against libc.so; I don't link against the vDSO. The VDSO provides gettimeofday with version LINUX_2.6. Since those versions don't match, the gettimeofday reference in my executable will not be satisfied by the definition in the vDSO. So at dynamic link time my program is always going to be linked with the gettimeofday in libc.so, which will in turn call the gettimeofday in the vDSO.

Am I missing something that makes the definition of gettimeofday with version LINUX_2.6 in the vDSO useful?

Ian

Quoted 25 lines · Ian Lance Taylor
Quoted 15 lines · H. Peter Anvin
Quoted 2 lines

If it doesn't, then you incur an additional indirection penalty. The

strong __vdso symbol allows the libc wrapper to fall back to the vdso implementation, the weak symbol allows three to be no wrapper at all. This is good.

Quoted 2 lines

The reason for changing ABI would be shifting types. This is very

much how glibc manages transitions.

The purpose of symbol versioning is so that symbols with well known names, like stat, can continue to use those same names while changing types. Both old and new programs can continue to use the name stat and continue to work even though they use different types.

Quoted 6 lines

I don't see how this applies to the kernel VDSO. Those symbols do not use well-known names; they use names like __vdso_time. If you change the types used by those symbols, you can change the name as well. What is the downside?

Ian

--
Sent from my mobile phone. Please pardon brevity and lack of formatting.

#21 lore raw

You can link against vdso.so if you want to; the kernel build provides an option to install that image. Doesn't mean that any particular libc uses it.

Quoted 51 lines · Ian Lance Taylor
Quoted 1 line · H. Peter Anvin

The weak symbols are well-known names. The __vdso symbols are

strong.

I see. But I don't understand how this is supposed to work. When I link a program against gettimeofday, I get a reference to gettimeofday with version GLIBC_2.2.5. After all, I only link against libc.so; I don't link against the vDSO. The VDSO provides gettimeofday with version LINUX_2.6. Since those versions don't match, the gettimeofday reference in my executable will not be satisfied by the definition in the vDSO. So at dynamic link time my program is always going to be linked with the gettimeofday in libc.so, which will in turn call the gettimeofday in the vDSO.

Am I missing something that makes the definition of gettimeofday with version LINUX_2.6 in the vDSO useful?

Ian

Quoted 1 line

On June 15, 2014 12:22:17 PM PDT, Ian Lance Taylor <[email protected]>

Quoted 1 line · wrote:
Quoted 1 line

On Sun, Jun 15, 2014 at 12:14 PM, H. Peter Anvin <[email protected]>

Quoted 2 lines · wrote:
Quoted 2 lines
Quoted 2 lines

If it doesn't, then you incur an additional indirection penalty.

The

Quoted 5 lines
Quoted 5 lines

strong __vdso symbol allows the libc wrapper to fall back to the vdso implementation, the weak symbol allows three to be no wrapper at all. This is good.

Quoted 2 lines

The reason for changing ABI would be shifting types. This is very

Quoted 8 lines
Quoted 8 lines

much how glibc manages transitions.

The purpose of symbol versioning is so that symbols with well known names, like stat, can continue to use those same names while changing types. Both old and new programs can continue to use the name stat and continue to work even though they use different types.

I don't see how this applies to the kernel VDSO. Those symbols do

not

Quoted 8 lines
Quoted 5 lines

use well-known names; they use names like __vdso_time. If you change the types used by those symbols, you can change the name as well. What is the downside?

Ian

--
Sent from my mobile phone. Please pardon brevity and lack of

formatting.

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#22 lore raw
Quoted 1 line · H. Peter Anvin

You can link against vdso.so if you want to; the kernel build provides an option to install that image. Doesn't mean that any particular libc uses it.

I'd be rather surprised if anyone does this, given the generally spotty state of vdso.so installation and that there's no such thing as an ELF import library.

Also, the vdso's clock_gettime isn't actually compatible with glibc/POSIX -- the return value and errno handling don't match.

--Andy

#23 lore raw

Yes, but the option exists.

Quoted 13 lines · Andy Lutomirski
Quoted 1 line · H. Peter Anvin

You can link against vdso.so if you want to; the kernel build

provides an option to install that image. Doesn't mean that any particular libc uses it.

I'd be rather surprised if anyone does this, given the generally spotty state of vdso.so installation and that there's no such thing as an ELF import library.

Also, the vdso's clock_gettime isn't actually compatible with glibc/POSIX -- the return value and errno handling don't match.

--Andy

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#24 lore raw
In reply to H. Peter Anvin
Quoted 4 lines · H. Peter Anvin

If it doesn't, then you incur an additional indirection penalty. The strong __vdso symbol allows the libc wrapper to fall back to the vdso implementation, the weak symbol allows three to be no wrapper at all. This is good.

No it doesn't. The weak symbol does not implement the interface contract of the libc function; on error it's required to set errno, and of course the kernel/vdso cannot do this. Also, there's no way the symbol versions the kernel provides can match up with userspace symbol versions that consumers of the function would need. So these features are just useless.

Rich

#25 lore raw
In reply to Andy Lutomirski

At least versioning is always useful to have, just to have another tool for compatibility if changes are needed.

Not sure about weak, but it can't hurt. It seems to be standard practice in glibc.

I haven't looked into this in detail, but my initial assumption would be that it wouldn't be useful to add new vdso interfaces just for Go. After all would you really want to force ever Go user to upgrade their kernel just to get fast fime? So it has to work with whatever is already there anyways.

I agree that current interface is not great (I didn't design it BTW, just adapted it from i386), but it should be implementable.

-Andi

Signature · 1 line
ak-VuQAYsv1563Yd54FQh9/[email protected] -- Speaking for myself only.
#26 lore raw
Quoted 3 lines · Andi Kleen

At least versioning is always useful to have, just to have another tool for compatibility if changes are needed.

I disagree. Just like syscalls, vdso functions with new APIs/ABIs should have new names. For example if a version of clock_gettime for a new userspace with 64-bit time_t is added, it should be called clock_gettime64 (with a corresponding __NR_clock_gettime64), not offered as a versioned symbol over top of the old one. The versioned symbol approach is a lot more confusing and as far as I can tell offers no benefits.

Rich

#27 lore raw
In reply to Andi Kleen
Quoted 6 lines · Andi Kleen

I haven't looked into this in detail, but my initial assumption would be that it wouldn't be useful to add new vdso interfaces just for Go. After all would you really want to force ever Go user to upgrade their kernel just to get fast fime? So it has to work with whatever is already there anyways.

Go works fine with the current interface. There was, arguably, a bug in Go's old implementation in that it assumed that the vDSO would have a normal symbol table. That bug has already been fixed.

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

So why not provide a simple interface?

Ian

#28 lore raw
Quoted 9 lines

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

So why not provide a simple interface?

What good would it do now that everyone already supports it?

The proposal is 10+ years too late.

-andi

#29 lore raw
Quoted 8 lines · Andi Kleen
Quoted 8 lines

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

Quoted 5 lines
Quoted 1 line

So why not provide a simple interface?

What good would it do now that everyone already supports it?

The proposal is 10+ years too late.

I addressed that in one of my first emails to this thread. I don't think adding it now is immediately useful, but it will be very nice to have in a few years when optimizing for kernels from 2014 and earlier is no longer a priority. I would expect new users of vdso at that time not to bother with the ELF parsing ugliness and simply to use the lookup interface. It's not like they'd be losing any functionality on old kernels, just some performance due to falling back to the syscall.

Rich

#30 lore raw
In reply to Andi Kleen
Quoted 11 lines · Andi Kleen
Quoted 9 lines

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

So why not provide a simple interface?

What good would it do now that everyone already supports it?

Do statically linked binaries use the vDSO calls?

Ian

#31 lore raw
Quoted 9 lines · Ian Lance Taylor
Quoted 8 lines · Andi Kleen
Quoted 8 lines

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

So why not provide a simple interface?

What good would it do now that everyone already supports it?

Do statically linked binaries use the vDSO calls?

Under glibc, I believe so (not checked). Under musl, yes, and even in the dynamic-linked case we use the same code that's used for static linking rather than trying to get the dynamic linker to do them correctly. I still have some cruft lying around from where (in the past) we tried to do it via the dynamic linker, but I'm probably going to remove that and make the vdso behave as RTLD_LOCAL so that there's no risk of weird symbols it exports interfering with the application (applications could still make it global via an explicit dlopen). The only reason for keeping it around at all in the dynamic linker is for the sake of gdb.

Rich

#32 lore raw
Quoted 16 lines · Rich Felker
Quoted 9 lines · Ian Lance Taylor
Quoted 8 lines · Andi Kleen
Quoted 8 lines

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

So why not provide a simple interface?

What good would it do now that everyone already supports it?

Do statically linked binaries use the vDSO calls?

Quoted 11 lines

Under glibc, I believe so (not checked). Under musl, yes, and even in the dynamic-linked case we use the same code that's used for static linking rather than trying to get the dynamic linker to do them correctly. I still have some cruft lying around from where (in the past) we tried to do it via the dynamic linker, but I'm probably going to remove that and make the vdso behave as RTLD_LOCAL so that there's no risk of weird symbols it exports interfering with the application (applications could still make it global via an explicit dlopen). The only reason for keeping it around at all in the dynamic linker is for the sake of gdb.

What about backtrace_symbols, dl_addr, and the unwinder (e.g. siglongjmp)? It would be nice to wean the vdso off of frame pointers some day.

--Andy

#33 lore raw
Quoted 28 lines · Andy Lutomirski
Quoted 16 lines · Rich Felker
Quoted 9 lines · Ian Lance Taylor
Quoted 8 lines · Andi Kleen
Quoted 8 lines

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

Quoted 5 lines
Quoted 3 lines
Quoted 1 line

So why not provide a simple interface?

What good would it do now that everyone already supports it?

Do statically linked binaries use the vDSO calls?

Quoted 10 lines

Under glibc, I believe so (not checked). Under musl, yes, and even in the dynamic-linked case we use the same code that's used for static linking rather than trying to get the dynamic linker to do them correctly. I still have some cruft lying around from where (in the past) we tried to do it via the dynamic linker, but I'm probably going to remove that and make the vdso behave as RTLD_LOCAL so that there's no risk of weird symbols it exports interfering with the application (applications could still make it global via an explicit dlopen). The only reason for keeping it around at all in the dynamic linker is for the sake of gdb.

Quoted 3 lines

What about backtrace_symbols, dl_addr, and the unwinder (e.g. siglongjmp)? It would be nice to wean the vdso off of frame pointers some day.

My comments were in the context of musl, which doesn't use unwinding internally whatsoever, but if you're asking about the usefulness of being able to see the vdso's elf/dwarf2/etc. stuff for various outside-of-libc purposes, then yes. My statement "for the sake of gdb" was just a poor substitute for this concept (debugging/introspective use in general).

Rich

#34 lore raw
In reply to Ian Lance Taylor
Quoted 20 lines · Ian Lance Taylor
Quoted 6 lines · Andi Kleen

I haven't looked into this in detail, but my initial assumption would be that it wouldn't be useful to add new vdso interfaces just for Go. After all would you really want to force ever Go user to upgrade their kernel just to get fast fime? So it has to work with whatever is already there anyways.

Go works fine with the current interface. There was, arguably, a bug in Go's old implementation in that it assumed that the vDSO would have a normal symbol table. That bug has already been fixed.

I think this issue started when some of the Go developers questioned why the kernel needed to provide a very complex interface--parsing an ELF shared shared library--for very simple functionality--looking up the address of a magic function. This approach has required special support not just in Go, but also in the dynamic linker and gdb, and does not work well for statically linked binaries. The support in gdb is perhaps a good idea, but elsewhere it does not make sense.

Quoted 1 line

So why not provide a simple interface?

This is my sentiment as well, but I think you've stated it better than I have.

Also note that, if the vdso is providing a "lookup the address of a magic function by name" function, it need not be a full ELF parser, i.e. it doesn't need to be able to deal with any ELF files except the vdso provided by the current kernel. This probably allows the parser to be considerably smaller; for example, all the version stuff can be omitted unless the actual kernel in use provides multiple symbol versions.

Rich

#35 lore raw
In reply to Andy Lutomirski
Quoted 48 lines · Andy Lutomirski
Quoted 5 lines · H. Peter Anvin
Quoted 4 lines · Rich Felker

Arguably, it was a mistake for the kernel to expose a virtual ELF to begin with, and it should just have exposed a "lookup function by name" operation to begin with. Yes this can be done in userspace,

but

Quoted 7 lines
Quoted 2 lines

I see it more as a matter of "fixing a broken API design".

What the fsck are you smoking? There is immense value in providing a stable and very well-defined data structure, which also happens to be what dynamic linkers already want to consume. Providing a helper for crippled libc applications has potential value. Shaving a few

hundred

Quoted 1 line

bytes off static applications is a very weak argument, simply because

it

Quoted 1 line

is such a small fraction of the enormous cost of a static

application,

Quoted 6 lines

and static applications are problematic in a number of other ways, especially the lack of ability to fix bugs.

Treating the kernel as an ersatz dynamic library for "static" applications is kind of silly -- after all, why not provide an entire libc in the vdso? I have actually seen people advocate for doing

that.

To be clear, I have no desire whatsoever to give the vdso an actual ELF parser or anything else that userspace should be providing itself. I think that a special-purpose vdso parser in the vdso makes some sense, though, since userspace might otherwise provide one for the sole purpose of parsing the vdso.

And there's plenty of reasons that having the vdso be an ELF image is useful. For one thing, gdb can take advantage of it. For another, CRIU is parsing it for a rather different reason, and something like __vdso_findsym won't fill that need.

Also, given the general lack of a comprehensible specification of what the GNU flavor of the ELF format actually is [1], there's something to be said for reducing the proliferation of ELF parsers. glibc and binutils are quite unlikely to become incompatible with each other, but I sincerely doubt that anyone from binutils land is likely to review (and maintain!) my ELF parser, Go's, or a hypothetical future ELF parser from any of the other glibc-less things. If those things use one that's in the kernel, then it's easy for the kernel to guarantee that each vdso image can successfully parse itself.

Quoted 3 lines

[1] The only comprehensible description of the GNU hash extension that I could find is on Oracle's blog (!)

Curious about this blog. We do have a GNU hash implementation in Syslinux, too, for another reference.

Signature · 1 line
Sent from my mobile phone.  Please pardon brevity and lack of formatting.
#36 lore raw
Quoted 51 lines · H. Peter Anvin
Quoted 48 lines · Andy Lutomirski
Quoted 5 lines · H. Peter Anvin
Quoted 4 lines · Rich Felker

Arguably, it was a mistake for the kernel to expose a virtual ELF to begin with, and it should just have exposed a "lookup function by name" operation to begin with. Yes this can be done in userspace,

but

Quoted 7 lines
Quoted 2 lines

I see it more as a matter of "fixing a broken API design".

What the fsck are you smoking? There is immense value in providing a stable and very well-defined data structure, which also happens to be what dynamic linkers already want to consume. Providing a helper for crippled libc applications has potential value. Shaving a few

hundred

Quoted 1 line

bytes off static applications is a very weak argument, simply because

it

Quoted 1 line

is such a small fraction of the enormous cost of a static

application,

Quoted 6 lines

and static applications are problematic in a number of other ways, especially the lack of ability to fix bugs.

Treating the kernel as an ersatz dynamic library for "static" applications is kind of silly -- after all, why not provide an entire libc in the vdso? I have actually seen people advocate for doing

that.

To be clear, I have no desire whatsoever to give the vdso an actual ELF parser or anything else that userspace should be providing itself. I think that a special-purpose vdso parser in the vdso makes some sense, though, since userspace might otherwise provide one for the sole purpose of parsing the vdso.

And there's plenty of reasons that having the vdso be an ELF image is useful. For one thing, gdb can take advantage of it. For another, CRIU is parsing it for a rather different reason, and something like __vdso_findsym won't fill that need.

Also, given the general lack of a comprehensible specification of what the GNU flavor of the ELF format actually is [1], there's something to be said for reducing the proliferation of ELF parsers. glibc and binutils are quite unlikely to become incompatible with each other, but I sincerely doubt that anyone from binutils land is likely to review (and maintain!) my ELF parser, Go's, or a hypothetical future ELF parser from any of the other glibc-less things. If those things use one that's in the kernel, then it's easy for the kernel to guarantee that each vdso image can successfully parse itself.

Quoted 6 lines
Quoted 3 lines

[1] The only comprehensible description of the GNU hash extension that I could find is on Oracle's blog (!)

Curious about this blog. We do have a GNU hash implementation in Syslinux, too, for another reference.

https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections

FWIW, I bet that __vdso_findsym could be smaller if it used the GNU hash. Maybe it would save about the same amount of space that turning on the GNU hash would take up.

--Andy

#37 lore raw
Quoted 4 lines · Andy Lutomirski
Quoted 4 lines
Quoted 3 lines

[1] The only comprehensible description of the GNU hash extension that I could find is on Oracle's blog (!)

Quoted 8 lines
Quoted 2 lines

Curious about this blog. We do have a GNU hash implementation in Syslinux, too, for another reference.

https://blogs.oracle.com/ali/entry/gnu_hash_elf_sections

FWIW, I bet that __vdso_findsym could be smaller if it used the GNU hash. Maybe it would save about the same amount of space that turning on the GNU hash would take up.

How so? My implementation of gnu hash lookup in musl's dynamic linker is somewhat larger than the sysv hash lookup, and that's even with skipping the bloom filter (which did not seem to yield any benefit, though we may revisit this issue later). The actual hash function is _slightly_ smaller for gnu, but the lookup function has a lot more work to do.

Of course for the size of vdso's symbol table, a pure linear search with no hash table would suffice...

Rich

#38 lore raw
In reply to H. Peter Anvin
Quoted 10 lines · H. Peter Anvin
Quoted 6 lines · Rich Felker

Arguably, it was a mistake for the kernel to expose a virtual ELF to begin with, and it should just have exposed a "lookup function by name" operation to begin with. Yes this can be done in userspace, but I see it more as a matter of "fixing a broken API design".

What the fsck are you smoking? There is immense value in providing a stable and very well-defined data structure,

I believe the widespread consensus is that there's more value in providing a well-defined interface and keeping your data structures opaque. Even if it's useful for the data structure to be there for the debugger's sake (debuggers have to know about binary formats, etc.) that's not an excuse for components of the system which have nothing to do with ELF to have to know about it.

Quoted 5 lines

which also happens to be
what dynamic linkers already want to consume. Providing a helper for crippled libc applications has potential value. Shaving a few hundred bytes off static applications is a very weak argument, simply because it is such a small fraction of the enormous cost of a static application,

No, it's not. It could very easily be 5% of the binary size for plenty of utilities that are useful to make static. Anyway it's not that a single ~450 byte function is such a cost for static binaries in itself; it's that if one is not vigilant against this kind of creep, it ends up not being one ~450 byte function but quickly 5, 10, 20, etc. of them, and eventually you get to the point (see: glibc) where static linking is useless.

Quoted 2 lines

and static applications are problematic in a number of other ways, especially the lack of ability to fix bugs.

This is a standard fallacy I won't address here because it's off-topic.

Quoted 3 lines

Treating the kernel as an ersatz dynamic library for "static" applications is kind of silly -- after all, why not provide an entire libc in the vdso? I have actually seen people advocate for doing that.

I consider that a very bad idea. What I think vdso should provide is just fast versions of syscalls that can be performed without entering kernelspace, but for which it's impossible to write such an implementation in userspace because it (a) has to interact with kernel-provided data structures that should not be public APIs, and
(b) requires hardware-specific versions of the function. However, I think there's a value in providing access to these syscall replacements in an easy-to-use manner that doesn't require that components which have nothing to do with ELF (i.e. the time functions) be aware of ELF data structures. And this is why I support the proposal.

Rich