Regarding "EBOs" that is the only binding that is important at the time that you call
glDrawElements (...). That is because a non-zero value assigned to
GL_ELEMENT_ARRAY_BUFFER means that the pointer you pass to that function will point to memory owned by the buffer object you have bound to
Vertex Buffers are different, you only have to bind a buffer object to
GL_ARRAY_BUFFER prior to calling
glVertexAttribPointer (...). Just like
glDrawElements (...), this function will setup a pointer to memory relative to the currently bound
glVertexAttribPointer (...) returns it no longer matters what is bound to
GL_ARRAY_BUFFER; you already established what the source of the vertex pointer's memory is and changing the thing bound to
GL_ARRAY_BUFFER after the fact will not change anything.
Long story short, your vertex pointers specify which VBO(s) is/are used and the
GL_ELEMENT_ARRAY_BUFFER binding establishes the EBO. You can coalesce multiple buffers into one and then draw specific ranges at a time if you want to do this more efficiently (e.g. fewer buffer object binds). The thing is, buffer object bindings are a really cheap state to change - draw calls become expensive primarily because you change other more complicated things like textures or render targets.